抱歉,我是Haskell的新手。这个问题可能很容易......
来自Hoogle,return
签名为return :: Monad m => a -> m a
head
签名为head :: [a] -> a
在我执行此head $ return [1,2,3]
之前,我认为ghci会抛出错误,因为m [a]
不是[a]
。但令我惊讶的是,它返回[1,2,3]
。并tail $ return [1,2,3]
返回[]
。为什么呢?
还有一个问题:
我写了一个函数来生成随机数:
drawFloat :: Float -> Float -> IO Float
drawFloat x y = getStdRandom (randomR (x,y))
randList = mapM (const $ drawFloat 2 10) [1..10] -- generate a list of random numbers
当我想获得列表的头部时,我首先尝试head randList
(失败),但head <$> randList
工作。什么是<$>
?谁能解释一下?谢谢!
答案 0 :(得分:12)
我认为ghci会抛出错误,因为
m [a]
与[a]
不同。
也许没有,但m [a]
和[b]
可以统一!例如,我们可以设置m ~ []
和b ~ [a]
,以便m [a] ~ [] [a] ~ [[a]]
和[b] ~ [[a]]
。然后,我们只需要检查[]
是否为Monad
。而这正是发生的事情:
> return [1,2,3] :: [[Int]]
[[1,2,3]]
然后应该清楚为什么head
会返回[1,2,3]
而tail
会返回[]
。
randList = mapM (const $ drawFloat 2 10) [1..n]
作为评论,尚未回答您的问题:这是更好的拼写replicateM n (drawFloat 2 10)
。
head randList
(失败),但head <$> randList
有效。什么是<$>
?
这里的问题是head
仍然期待一个列表。之前,当您使用return
时,monad尚未被选中,因此可以选择[]
;但是在这里,很明显你使用的monad是IO
。所以head
无法取得进展。解决方案是教head
如何处理IO
。有很多方法,<$>
就是其中之一;它有这种类型:
> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
阅读本文的方法是:给定一个纯函数,教它如何处理IO
(或任何其他类似的效果Functor
)。例如,它也有这种类型:
(<$>) :: ([a] -> a) -> IO [a] -> IO a
还有其他几种教师。两个常用的是<*>
和=<<
:
Prelude Control.Applicative> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Prelude Control.Applicative> :t (=<<)
(=<<) :: Monad m => (a -> m b) -> m a -> m b
读取第一个:给定一个产生纯函数的有效计算,产生一个可以处理效果的函数。第二个应该理解为:给定一个在产生输出之前有一些效果的函数,产生一个可以处理效果的函数。
此时,如果这些解释没有帮助,您应该转向网络上其他地方提供的众多excellent monad tutorials之一。我个人的最爱是You Could Have Invented Monads!和All About Monads。
答案 1 :(得分:3)
关于你的第一个问题:
return
获取一个“原始”值,并为其提供一个值为“wrapped”的monad。由于您在head
上使用tail
和return [1,2,3]
,因此相关的monad是列表monad []
。所以在这种情况下,
return [1,2,3] == [[1,2,3]]
在其上应用head
和tail
,您将分别获得[1,2,3]
和[]
。
关于第二个问题,请注意randList
的类型为IO [Float]
。所以它不再是一个列表,而是monad中的列表。要将任何函数应用于monad的内容,您可以使用fmap
(<$>
是其中缀速记),其类型为:
fmap :: Functor f => (a -> b) -> f a -> f b
A monad is always a Functor。那么fmap
所做的是,它需要一个普通函数,(在你的例子中是head
)和一个monadic值,将函数应用于monad的“内容”,并返回一个返回 - 函数的值包含在同一个monad f
中。