Haskell 创建用于增量存储结果类型不匹配的monad
我试图通过创建单子来更好地理解单子。其想法是创建一个只存储所有结果的monad。然而,我就是找不到匹配的类型Haskell 创建用于增量存储结果类型不匹配的monad,haskell,monads,Haskell,Monads,我试图通过创建单子来更好地理解单子。其想法是创建一个只存储所有结果的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
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)
main::IO()
main=do
让
v1=返回值(1,1)
v2=返回值(8,8)
x=移动v1 v2
打印x
新类型Accum a=Accum{open::(a[a])}
派生(显示)
实例Monad Accum在哪里
返回v=累计(v,[v])
(>>=)mf=let(r1,l1)=开m
r2=f r1
(r3,l3)=打开r2
累计(r3,l1++l3)
移动::Accum(Int,Int)->Accum(Int,Int)->Accum(Int,Int)
移动p1 p2=do
(x1,y1)您的代码与Writer[a]
monad非常相似,我认为除了命名之外,没有比缩小所有方面的差距更简单的修复方法了。具体而言:
由于monad必须允许任意结果类型,因此效果部分的类型不能依赖于结果部分的类型,因此将类型定义更改为
newtype Accum a b = Accum { open :: (b, [a]) }
为了满足monad定律,return
不能具有非平凡的效果,因此将其定义更改为
return v = Accum (v, [])
现在您需要有一个显式的操作来存储,也就是MonadWriter中的tell
:
tell as = Accum ((), as)
最后,根据需要更改类型签名以包含额外的类型参数。我将您的请求理解为
创建一个记录所有中间结果的单子
这样的事情在哈斯克尔是不可能存在的。首先,单子必须是函子。我想你已经知道那是什么了
我只看到fmap
的两个实现大致保持了“记录计算”语义并通过了类型检查:
fmap f (Accum (x,xs)) = Accum (f x, map f xs) -- map history
fmap f (Accum (x,xs)) = Accum (f x,[]) -- forget history
第一种方法使您的acum a
与非空列表相同[a]
,众所周知,列表是monad实例;不完全是你想要的。
第二种更为乏味
当您使用bind(>=
)从函子转换为单子时,情况并没有变得更好
正如厄尔詹·约翰森已经指出的那样,ma
中的a
可以是任何类型。当你想更好地理解单子时,不妨这样做:在ma
中,让a
成为ma
本身。现在你有了m(ma)
。对于每个单子,都有一个方法
join :: m (m a) -> m a
这将删除一级嵌套。可以将列表列表转换为列表,将树树树转换为树,并通过逐个执行将生成IO操作的IO操作转换为单个操作
我必须克制住再写一篇蹩脚的单子教程的冲动…我相信这样的例子会违反单子定律,例如,return x>>=return
并不等同于return x
。你确定你想要的不是这个吗?可能是,但我只想要一个简单的简化版本,看看它是如何连接在一起的。