正确定义 monad class

Properly defining a monad class

如何使列表 monad 的以下声明可编译?

module Main where

instance Monad m where
  -- "return" constructs a one-item list.
  return x = [x]
  -- "bind" concatenates the lists obtained by applying f to each item in list xs.
  xs >>= f = concat (map f xs)
  -- The zero object is an empty list.
  mzero = []

目前我收到以下错误:

monad_ex1.hs:9:3: ‘mzero’ is not a (visible) method of class ‘Monad’

我的代码来自 https://en.wikipedia.org/wiki/Monad_(functional_programming)#Collections ,目标是 运行 从中创建可编译代码,将其导入 ghci 并使用它。

从代码中删除 mzero 会导致另一个神秘消息:

Illegal instance declaration for ‘Monad m’
      (All instance types must be of the form (T a1 ... an)
       where a1 ... an are *distinct type variables*,
       and each type variable appears at most once in the instance head.
       Use FlexibleInstances if you want to disable this.)
    In the instance declaration for ‘Monad m’

这里有一些潜在的陷阱,其中大部分都包含在评论中:

  • 没有为 Monad 个实例定义 mzero,如果您尝试指定一个实例,将会出现错误。 mzero 将在 MonadPlus 实例中定义,如果有的话。
  • 尝试为内置列表重新定义 monad 实例是行不通的。如果你想玩这个,你需要定义你自己的 List 类型。
  • 在"modern"Haskell(从GHC 7.10开始),执行Applicative => Monad Proposal has obsoleted a lot of old Monad tutorials and made user-defined Monads a little more difficult to write because you also need to define Functor and Applicative instances (detailed migration instructions).

因此,这里是您的列表示例的翻译,使用用户定义的 List 类型和 GHC 7.10 兼容的样板。请注意,return 的定义移到了 Applicative 实例的 pure 中。

module MyListMonad where

import Control.Monad

data List a = Empty | Cons a (List a) deriving (Show, Eq)

instance Functor List where
  fmap = liftM                      -- boilerplate
instance Applicative List where
  pure x = Cons x Empty             -- put definition of `return` here
  (<*>) = ap 
instance Monad List where
  return = pure                     -- boilerplate
  (>>) = (*>)                       -- boilerplate
  xs >>= f = myConcat (myMap f xs)  -- bind definition here

myConcat :: List (List a) -> List a
myConcat (Cons lst rest) = myAppend lst (myConcat rest)
myConcat Empty = Empty

myAppend :: List a -> List a -> List a
myAppend (Cons x rest) ys = Cons x (myAppend rest ys)
myAppend Empty ys = ys

myMap :: (a -> b) -> List a -> List b
myMap f (Cons x rest) = Cons (f x) (myMap f rest)
myMap _ Empty = Empty

test = do x <- Cons 1 $ Cons 2 $ Cons 3 Empty
          y <- Cons 4 $ Cons 5 $ Empty
          return (x * y)