2011-12-17 104 views
6

我在爲我組成的數據類型做實例聲明時使用了一個類型上下文。函數上需要實例聲明的Haskell類型上下文

data Set a = Insert a (Set a) | EmptySet 

instance (Show a) => Show (Set a) where 
    show x = "{" ++ show' x ++ "}" where 
     show' (Insert x EmptySet) = show x 
     show' (Insert x xs) = show x ++ ", " ++ show' xs 

instance Eq a => Eq (Set a) where 
    (Insert x xs) == (Insert y ys) = (x == y) && (xs == ys) 

所以,現在,我要的EQ類型的上下文添加到所有的我定義使用我的設置類型,像這樣的功能,或者我得到一個錯誤類型:

memberSet::Eq a =>a->Set a->Bool 
memberSet _ EmptySet = False 
memberSet x (Insert y ys) 
    | x == y = True 
    | otherwise = memberSet x ys 

subSet::Eq a=>Set a->Set a->Bool 
subSet EmptySet _ = True 
subSet (Insert a as) bs 
    | memberSet a bs = subSet as bs 
    | otherwise = False 

錯誤我看起來像:

No instance for (Eq a) 
     arising from a use of `==' 
    In the expression: (x == y) 
    In a stmt of a pattern guard for 
       an equation for `memberSet': 
     (x == y) 
    In an equation for `memberSet': 
     memberSet x (Insert y ys) 
      | (x == y) = True 
      | otherwise = memberSet x ys 
Failed, modules loaded: none. 

這是什麼意思?爲什麼我會得到這個錯誤?我想,一旦我做了實例聲明,Haskell就能夠自動驗證在我的函數「memberSet」和「subSet」中被「==」比較的東西是否會自動被檢查爲「Eq?」的實例。

編輯爲清楚:

我的問題是,我不明白爲什麼類型的上下文是在「成員集」的和必要的「子集」。如果我像這樣刪除它,它不會編譯。

memberSet::a->Set a->Bool 
    memberSet _ EmptySet = False 
    memberSet x (Insert y ys) 
     | x == y = True 
     | otherwise = memberSet x ys 

    subSet::Set a->Set a->Bool 
    subSet EmptySet _ = True 
    subSet (Insert a as) bs 
     | memberSet a bs = subSet as bs 
     | otherwise = False 
+0

你給我typechecks的代碼。你要離開什麼? – bdonlan 2011-12-17 23:33:45

+0

我懷疑涉及範圍或名稱的某種相當微妙的錯誤,因爲給出的代碼似乎很好。 – 2011-12-17 23:36:40

+0

我的問題還不清楚。代碼是編譯。我想知道爲什麼它不能用編輯的「memberset」和「subset」類型上下文進行編譯。 – 2011-12-17 23:37:36

回答

4

您的實例聲明說那是什麼Set aEq實例每當a是。事實上,a實際上是否是Eq的實例完全是另一回事;這隻允許您將兩個Set==進行比較,而在memberSet中,您只是比較元素。

考慮類型Integer -> Integer。這不是Eq的實例,原因很明顯。如果Set包含該類型的元素,您會如何期待memberSet工作?

我想知道你希望在這裏完成的是確保只有Set s與元素類型是Eq的實例可以創建。如果是這樣,這是一個非常不同的問題,但也大多是不必要的 - 使用Set s的功能限制Eq s最終達到相同的目的。

爲什麼不看看the standard Data.Set module?請注意,它在集合上運行的大多數函數都具有Ord約束,反映了使用的內部表示是二叉搜索樹的事實。

+0

謝謝。我只是想讓自己熟悉Haskell。我只是大致翻譯了SICP的一個實現。我並沒有試圖完成任何特別的事情。我還留下了一些代碼,我希望能夠比較集合的平等。這就是爲什麼它是方程式的一個實例。要做到這一點,我想所有必須是Eq – 2011-12-17 23:57:39

+0

@Josh的實例:那麼你很幸運,因爲你現在正式更熟悉類型約束如何工作。 :]是的,比較兩組需要比較他們的元素,所以你對這部分是正確的。 – 2011-12-17 23:59:43

7

只是爲了好玩,你可以安排一下,這樣的限制是沒有必要的功能,通過使用GADT

{-# LANGUAGE GADTs #-} 
module Set where 

data Set x where 
    EmptySet :: Set a 
    Insert :: Eq a => a -> Set a -> Set a 

instance Show a => Show (Set a) where 
    show EmptySet = "{}" 
    show xs = "{" ++ show' xs ++ "}" 
     where 
     show' (Insert a EmptySet) = show a 
     show' (Insert a ys) = show a ++ ", " ++ show' ys 

instance Eq (Set a) where 
    (Insert x xs) == (Insert y ys) = x == y && xs == ys 
    EmptySet == EmptySet = True 
    _ == _ = False 

memberSet :: a -> Set a -> Bool 
memberSet x (Insert y ys) = x == y || memberSet x ys 
memberSet _ _ = False 

subSet :: Set a -> Set a -> Bool 
subSet EmptySet _ = True 
subSet (Insert a as) bs 
    | memberSet a bs = subSet as bs 
    | otherwise = False 

通過將Eq約束的Insert構造類型,我們可以確保

  1. 沒有非空集可以構造不在Eq類型。
  2. 只要我們在Insert構造函數上進行模式匹配,就可以使用Eq上下文(和字典),所以我們不需要在函數的類型簽名中提及它。
+0

哇,我不知道你可以這樣做......所以,當我這樣做時,我不必聲明類型構造函數嗎? – 2011-12-18 00:07:38

+0

你的意思是'我不必在那裏聲明類型類',而不是'類型構造函數'?如果是這樣的話,當在構造函數上進行模式匹配時,你在'GADT'中的值構造函數的約束是可用的,所以它們不需要在函數的類型簽名中重複。其他限制當然要在簽名中給出。但請注意,上下文僅在_pattern matching_上可用,而不是在使用該類型值的任何地方。 – 2011-12-18 00:16:09

+0

@Josh:如果你想知道像「Insert」和「EmptySet」這樣的數據構造函數,那些依然存在。這只是定義它們的替代語法。 – 2011-12-18 01:05:07