创建用于增量存储结果类型不匹配的 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 教程的冲动...
我试图通过创建一个 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 教程的冲动...