创建用于增量存储结果类型不匹配的 monad

Creating a monad for incrementally storing results type not matching

我试图通过创建一个 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 为我完成累积结果的副作用?

您的代码与 Writer [a] monad 非常相似,我认为没有比缩小除命名之外的差距更简单的修复方法了。具体来说:

由于monad必须允许任意结果类型,效果部分的类型不能依赖于结果部分的类型,所以将类型定义更改为

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

为了满足 monad 法则,return 不能有不平凡的效果,所以将其定义更改为

return v = Accum (v, [])

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

tell as = Accum ((), as)

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

我把你的请求读成了

Create a Monad that records all intermediate results.

haskell 中不可能存在这样的东西。首先,monad 必须是一个函子。我想你已经知道那是什么了。

我只看到 fmap 的两个实现大致保持 "record computation" 语义并通过类型检查:

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 实例;不完全是你想要的。 第二个更沉闷

当您从函子转向使用绑定的 monad (>>=) 时,情况并没有好转。

正如 Ørjan Johansen 所说,m a 中的 a 可以是 any 类型。当您想更好地理解 monad 时,可以这样做:在 m a 中让 a 成为 m a 本身。现在你有 m (m a)。对于每个 monad,都有一个方法

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

删除一层嵌套。一个列表的列表可以变成一个列表,一棵树变成一棵树,一个产生 IO-Action 的 IO-Action 可以通过一个接一个地执行变成一个单一的动作。

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