2014-06-30 19 views
6

假設我有一個這樣的數據類型:有條件獲得顯示存在類型參數的類型構造

{-# LANGUAGE RankNTypes #-} 
data X a = forall b. Show b => X (a b) 

我想獲得Show (X a),但當然我只能這樣做,如果有一個實例Show (a b)。我很想寫

{-# LANGUAGE StandaloneDeriving #-} 
deriving instance Show (a b) => Show (X a) 

,但因爲它是由FORALL不幸的約束變量類型b是不是在實例上下文中可用。

我的下一個嘗試是把Show (a b)背景下移動到FORALL中的數據類型定義,就像這樣:

data X a = forall b. Show (a b) => X (a b) 
deriving instance Show (X a) 

這將編譯,但不幸的是,現在我已經失去了構建有一個X的能力unshowable (a b)

是否有任何方法允許X與任何(a b)構建,然後有條件地推導Show (X a)只有當(a b)顯示?

回答

7

這是Prelude類的一個缺陷。圍繞它有一個很好的方法,但體現在prelude-extras包。我將在下面概述它。

我們想創建一個更高級的Show類。它看起來像這樣

class Show1 a where 
    show1 :: Show b => a b -> String 

那麼我們至少可以準確地表達我們所期望的約束像

deriving instance Show1 a => Show (X a) 

不幸的是,編譯器還沒有足夠的信息來實現這個推導。我們需要證明(Show b, Show1 a)足以推導出Show (a b)。要做到這一點,我們需要啓用一些(嚇人,但三立使用的)擴展

{-# LANGUAGE FlexibleInstances   #-} 
{-# LANGUAGE OverlappingInstances  #-} 

instance (Show b, Show1 a) => Show (a b) where 
    show = show1 

而現在,我們已經證明了這一點,編譯器將能夠得到我們所需要的

data X a = forall b . Show b => X (a b) 
deriving instance Show1 a => Show (X a) 
+0

太糟糕了。 GHC手冊中關於OverlappingInstances的所有內容都很可怕。 – Will

+0

是的,這不是很好的建議。 Show1也可以很容易地打開其他實例。 –

5

我會對J. Abrahamson的回答採取類似但略有不同的方法。

正是你問的不能做,因爲類型類需要解決的問題靜態,但Show (a b)的存在可能是動態取決於b的instantation。該實例化被隱藏在X值內,因此當您只有未知來源的X b時,類型檢查器不可見。

這將是很好寫的條件上aShow (a b)存在每當Show b呢,因爲那時的Show (a b)的存在實際上並不依賴於b,因爲我們已經知道,Show b始終是真實的。

我們不能直接寫條件,但我們可以用GADTs表達類似的東西:

{-# LANGUAGE GADTs #-} 
data ShowDict a where 
    ShowDict :: Show a => ShowDict a 

ShowDict a類型提供了一個排序Show a類的物化 - 它的東西,我們可以通過周圍,定義功能。

特別是我們現在可以定義一個Show1類表達的條件Show (a b)每當我們有一個Show b

class Show1 a where 
    show1Dict :: ShowDict b -> ShowDict (a b) 

現在我們可以通過構建ShowDict (a b),然後模式匹配在Show1,來定義Show (X a)在其上顯示Show實例:

{-# LANGUAGE ScopedTypeVariables #-} 
instance Show1 a => Show (X a) where 
    show (X (v :: a b)) = 
     case show1Dict ShowDict :: ShowDict (a b) of 
      ShowDict -> "X (" ++ show v ++ ")" 

更完整的實施還包括lude ShowshowsPrecshowList)的其他成員。

這個解決方案的好處是,我們可以很容易地定義Show1[],自動重用底層Show實例:

instance Show1 [] where 
    show1Dict ShowDict = ShowDict 

我也寧願避免J.亞伯拉罕的答案非常通用Show (a b)例如,但是不得不將Show實例中的邏輯放入X的缺點是我們最終不得不手動實現它,而不是爲構造函數獲取自動派生的行爲。

+0

我一般同意這個答案,但值得注意的是這些解決方案失去了派生。但這可能只是一個不可避免的後果。派生和存在經常不會很好地發揮作用。 –

+0

我實際上已經忘記了在'Show'實例中顯示'X'構造函數,這對您的答案有很大的好處。但是,是的,通常不可能使用派生存在。 –

相關問題