我想实现一个函数iterateM,其类型如下所示:
iterateM :: Monad m => (a -> m a) -> a -> [m a]
然而,我第一次写这个函数:
iterateM f x = f x >>= (\x' -> return x' : iterateM f x')
给我错误:
Could not deduce (m ~ [])
from the context (Monad m)
bound by the type signature for
iterateM :: Monad m => (a -> m a) -> a -> [m a]
at main.hs:3:1-57
`m' is a rigid type variable bound by
the type signature for
iterateM :: Monad m => (a -> m a) -> a -> [m a]
at main.hs:3:1
Expected type: [a]
Actual type: m a
In the return type of a call of `f'
In the first argument of `(>>=)', namely `f x'
In the expression: f x >>= (\ x' -> return x' : iterateM f x')
如果我删除了我的类型签名,ghci告诉我我的函数类型是:
iterateM :: Monad m => (a -> [a]) -> a -> [m a]
我在这里缺少什么?
答案 0 :(得分:12)
我从你的签名中收集到的内容:
iterateM :: (Monad m) => (a -> m a) -> a -> [m a]
n
元素iterateM f x
是否会成为f
n
次运行的动作。这非常接近iterate
,我怀疑我们可以用它来实现它。
iterate :: (b -> b) -> b -> [b]
iterate
为我们提供了b
的列表,我们想要一个m a
的列表,因此我怀疑b = m a
。
iterate :: (m a -> m a) -> m a -> [m a]
现在我们需要一种方法将f :: a -> m a
转换为m a -> m a
类型的内容。幸运的是,这正是bind的定义:
(=<<) :: (Monad m) => (a -> m b) -> (m a -> m b)
所以:
\f -> iterate (f =<<) :: (a -> m a) -> m a -> [m a]
要将我们的初始x :: a
放入所需的m a
,我们可以使用return
:
return :: (Monad m) => a -> m a
所以:
iterateM f x = iterate (f =<<) (return x)
点自由尝试。
答案 1 :(得分:1)
您递归使用iterateM会强制它在列表monad中。您需要运行iterateM操作并返回其结果。
尝试:
iterateM f x = do
x' <- f x
xs <- iterateM f x'
return $ x':xs