创建monad以逐步存储结果类型不匹配

时间:2015-02-12 02:18:59

标签: haskell monads

我试图通过尝试创建monad来更好地理解monad。我们的想法是创建一个只存储所有结果的monad。但是,我无法获得匹配的类型。

main :: IO ()
main = do
    let 
        v1 = return (1,1)
        v2 = return (8,8)
        x = move v1 v2
    print x


newtype Accum a = Accum { open :: (a, [a]) }
  deriving (Show)

instance Monad Accum where
    return v = Accum (v, [v])
    (>>=) m f = let (r1, l1) = open m
                    r2       = f r1
                    (r3, l3) = open r2
                in Accum (r3, l1 ++ l3)

move :: Accum (Int,Int) -> Accum (Int,Int) -> Accum (Int,Int)
move p1 p2 = do
    (x1,y1) <- p1
    (x2,y2) <- p2
    return (x1+x2, y1+y2)

Accum (r3, l1 ++ l3)行中,l1属于a类型,其中l3始终为b类型。我怎样才能让monad为我做累积结果的副作用?

2 个答案:

答案 0 :(得分:2)

你的代码与Writer [a] monad非常相似,我不认为有一种更简单的方法来修复它,而不是缩小除命名之外的差距。具体做法是:

由于monad必须允许任意结果类型,因此效果部分的类型不能取决于结果部分的类型,因此将类型定义更改为

newtype Accum a b = Accum { open :: (b, [a]) }

要实现monad法律,return不得产生重大影响,因此请将其定义更改为

return v = Accum (v, [])

现在您需要有一个明确的操作来存储,tell来自MonadWriter

tell as = Accum ((), as)

最后,根据需要更改类型签名以包含额外的类型参数。

答案 1 :(得分:1)

我将您的请求视为

  

创建一个记录所有中间结果的Monad。

这样的事情在haskell中不存在。首先,monad必须是一个仿函数。我假设你已经知道那是什么了。

我只看到fmap的两个实现大致保持“记录计算”语义和传递类型检查:

fmap f (Accum (x,xs)) = Accum (f x, map f xs) -- map history
fmap f (Accum (x,xs)) = Accum (f x,[]) -- forget history

第一个使Accum a与非空列表[a]相同,众所周知列表是monad实例;不是你想要的。 第二个甚至更无聊。

当你从仿函数转向使用bind(>>=)的monad时,情况就不会好转了。

正如ØrjanJohansen所说,a中的m a可以是任何类型。如果您希望更好地了解monad,请注意以下事项:m aa成为m a本身。现在你有m (m a)。对于每个monad,都有一种方法

join :: m (m a) -> m a

删除一级嵌套。可以将列表列表转换为列表,将树树转换为树,以及生成IO-Action的IO-Action可以通过一个接一个地执行来将其转换为单个操作。

我必须抵制写另一个糟糕的monad教程的冲动......