2016-01-06 61 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,破坏了所有人的代码,但改进了未来。这就是你所看到的。

相关问题