我试图通过尝试创建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为我做累积结果的副作用?
答案 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 a
让a
成为m a
本身。现在你有m (m a)
。对于每个monad,都有一种方法
join :: m (m a) -> m a
删除一级嵌套。可以将列表列表转换为列表,将树树转换为树,以及生成IO-Action的IO-Action可以通过一个接一个地执行来将其转换为单个操作。
我必须抵制写另一个糟糕的monad教程的冲动......