2015-09-20 74 views
3

鑑於Free Monad定義平等實例免費單子

data Free f a = Var a 
       | Node (f (Free f a)) 

我試圖爲它定義一個Eq實例:

instance (Functor f, Eq (f a)) => Eq (Free f a) where 
    (==) (Var x) (Var y)  = x == y 
    (==) (Node fu1) (Node fu2) = fu1 == fu2 
    (==) _ _     = False 

但是,這無法編譯:

FreeMonad.hs:17:10: 
    Non type-variable argument in the constraint: Eq (f a) 
    (Use FlexibleContexts to permit this) 
    In the context: (Functor f, Eq (f a)) 
    While checking an instance declaration 
    In the instance declaration for ‘Eq (Free f a)’ 
Failed, modules loaded: none. 

指定一個約束/前提條件(Functor f, Eq (f a))對我來說似乎很奇怪(至少我不瘦k我以前曾將它看作是初學者)。

如何爲Free f a定義一個Eq實例?

回答

11

Eq (f a)這樣的約束沒有任何問題。由於錯誤消息說,您將需要啓用(無害)FlexibleContexts GHC擴展做到這一點,所以加...

{-# LANGUAGE FlexibleContexts #-} 

...到源文件的頂部。

但請注意,(Functor f, Eq (f a))並不真實反映您在執行(==)時所做的工作。首先,你不需要這裏假設fFunctor,所以你可以放心地刪除Functor f約束。其次,約束條件應該符合你寫出不同情況的要求。在第一種情況下,你做x == yxy都是a類型,所以你需要Eq a。出於類似的原因,第二種情況需要Eq (f (Free f a))而不是Eq (f a)。這意味着你將結束與...

(Eq (f (Free f a)), Eq a) => Eq (Free f a) 

...它匹配的參考實現,如一個在Control.Monad.Free

+0

現在我看到:'變量'f'在約束條件下比實例頭 更經常出現:Eq(f(Free f a))。 (使用UndecidableInstances來允許)'。還可以添加嗎? –

+0

@KevinMeredith也可以。 UndecidableInstances可能發生的最糟糕的事情就是編譯失敗。將類型檢查器引向無限循環,但這不會發生在這裏。 GHC建議他們通常不應該打開的主要討厭擴展是[OverlappingInstances](http://stackoverflow.com/q/1064232)和[IncoherentInstances](http://stackoverflow.com/q/16050566) - 這兩個人爲廢話敞開大門。順便說一下,你做了正確的事情:BTW:大多數擴展是無害的,但你應該知道它們在做什麼以及在啓用之前是否需要它們。 – duplode

2

duplode顯示如何用靈活的上下文來做到這一點。如果你想要Haskell 2010,通常的做法是使用Prelude.Extras或類似的Eq1類。

class Eq1 f where 
    (==#) :: Eq a => f a -> f a -> Bool 

然後你會使用

instance (Eq1 f, Eq a) => Eq (Free f a) where ... 
instance Eq1 f => Eq1 (Free f) -- default instance is fine. 

我不是在我的電腦現在,所以我不能對此進行測試,直到後來。