2016-01-06 40 views
3

我試圖從this paper第一章,它是這樣實現的例子時:得到一個錯誤「應用型的無實例」聲明一個單子

data Tree a = Fork (Tree a) (Tree a) | Leaf a | Nil deriving (Show) 

instance Monad Tree where 
    return a = Leaf a 
    Nil >>= f = Nil 
    Leaf a >>= f = f a 
    Fork u v >>= f = Fork (u >>= f) (v >>= f) 

tree1 = Fork 
     (Fork (Leaf 2) Nil) 
     (Fork (Leaf 2) (Leaf 3)) 

tree2 = Fork (Leaf 2) (Leaf 3) 

f 2 = Fork Nil (Leaf "Two") 
f 3 = Fork (Leaf "Three") (Leaf "String") 

tree3 = tree2 >>= f 

當我在GHC運行它,我得到這個錯誤:

monads.hs:3:10: 
    No instance for (Applicative Tree) 
     arising from the superclasses of an instance declaration 
    In the instance declaration for ‘Monad Tree’ 
Failed, modules loaded: none. 

我嘗試添加這開始

class Monad m where 
    return :: a -> m a 
    (>>=) :: m a -> (a -> m b) -> m b 

但我得到這個錯誤:

monads.hs:7:10: 
    Ambiguous occurrence ‘Monad’ 
    It could refer to either ‘Main.Monad’, defined at monads.hs:1:1 
          or ‘Prelude.Monad’, 
          imported from ‘Prelude’ at monads.hs:1:1 
          (and originally defined in ‘GHC.Base’) 

什麼是最正確的修復?

+1

您不能定義一個類('Monad '或別的東西)兩次...... –

+2

自那篇論文以來,'Monad'獲得了'Applicative'的依賴。不過,你應該能夠使用'ap'和'return'來實現它。 –

+1

要真正明確地說明問題,您遇到的問題是此代碼在較早版本的Haskell中工作,但在GHC的最新版本中不再使用。最近的版本不允許你定義一個'Monad'實例,除非有問題的類型也有'Functor'和'Applicative'實例。 –

回答

5

問題是,像documentation指定的那樣。該Monad類的簽名是:

class Applicative m => Monad m where 
    --... 

這意味着,爲了定義一個類型的實例是Monad,你首先需要定義一個類型爲Applicative。這個問題就更加嚴重,因爲signature of Applicative狀態:

class Functor f => Applicative f where 
    --... 

所以,你首先需要做TreeFunctor一個實例。之所以在這篇論文中沒有必要,是因爲 - 據我所知,在Prelude的早期版本中,這些約束是沒有必要的。

現在爲了讓它工作,我們首先使TreeFunctor的一個實例。因此,我們需要定義一個函數fmap,其中 - 對於一個給定函數f :: a -> b,映射一個Tree aTree b

instance Functor Tree where 
    fmap f (Leaf a) = Leaf $ f a 
    fmap f (Fork u v) = Fork (fmap f u) (fmap f v) 
    fmap _ Nil = Nil 

現在我們已經定義了這一點,我們可以定義Applicative

instance Applicative Tree where 
    pure = Leaf 
    (<*>) (Leaf f) = fmap f 
    (<*>) Nil = const $ Nil 

最後我們可以定義Monad實例:

instance Monad Tree where 
    return a = Leaf a 
    Nil >>= f = Nil 
    Leaf a >>= f = f a 
    Fork u v >>= f = Fork (u >>= f) (v >>= f) 
+0

是否所有monad實例都需要這種分層定義? (Functor,Applicative,Monad)? –

+2

@MarkKaravan:如果你使用'base'的最新版本('Prelude'中的大部分東西),你需要定義它們全部三個。但是 - 和大多數編程語言一樣 - 當然你可以按照你喜歡的任何順序在你的文件中編寫它們。 –

+2

@MarkKaravan,幸運的是,大多數人發現'Monad'實例聲明只是他們代碼的一小部分。 – dfeuer

8

要擴展Louis Wasserman的評論,現在當您聲明Monad實例時,您需要添加一個Applicative(因此Functor)實例。一旦你寫了Monad情況下,其他情況都是一樣的:

import Control.Monad (liftM, ap) 

instance Functor Tree where 
    fmap = liftM 
instance Applicative Tree where 
    pure = return 
    (<*>) = ap 

這種改變,因爲每個MonadApplicative(使用這個例子),而不是周圍的其他方式,所以這是道德上的超類。然而,在Monad之後Applicative被添加到標準庫中,所以它很長一段時間沒有成爲真正的超類,因爲它會破壞人們的代碼。最近,由於Applicative已經非常普遍的使用,社區決定讓Applicative成爲一個真正的超類Monad,破壞了所有人的代碼,但改進了未來。這就是你所看到的。

相關問題