更新:好的,这个问题可能非常简单。
q <- mapM return [1..]
为什么这永远不会回来?
mapM是不是懒得处理无限列表?
以下代码挂起。但是,如果我用线B替换A线,它就不会再挂起了。或者,如果我在A行之前加上“splitRandom $”,它也不会挂起。
Q1是:mapM不是懒惰吗?否则,为什么用线B替换A行“修复此”代码?
Q2是:为什么在使用splitRandom的A行之前“解决”了这个问题?
import Control.Monad.Random
import Control.Applicative
f :: (RandomGen g) => Rand g (Double, [Double])
f = do
b <- splitRandom $ sequence $ repeat $ getRandom
c <- mapM return b -- A
-- let c = map id b -- B
a <- getRandom
return (a, c)
splitRandom :: (RandomGen g) => Rand g a -> Rand g a
splitRandom code = evalRand code <$> getSplit
t0 = do
(a, b) <- evalRand f <$> newStdGen
print a
print (take 3 b)
代码懒惰地生成无限的随机数列表。然后它生成一个随机数。通过使用splitRandom,我可以在无限列表之前首先评估后一个随机数。如果我在函数中返回b而不是c,则可以证明这一点。
但是,如果我将mapM应用于列表,程序现在会挂起。为了防止这种情况发生,我必须在mapM之前再次应用splitRandom。我的印象是mapM可以懒洋洋地
答案 0 :(得分:31)
嗯,懒惰,然后 lazy 。 mapM
确实是懒惰的,因为它没有做更多的工作。但是,请查看类型签名:
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
想想这意味着什么:你给它一个函数a -> m b
和一堆a
。常规map
可以将这些变为一堆m b
,但不是m [b]
。将b
合并为单个[b]
而不使monad妨碍的唯一方法是使用>>=
对m b
进行排序以构建列表。
事实上,mapM
恰好等同于sequence . map
。
通常,对于任何monadic表达式,如果完全使用该值,则必须强制导致表达式的整个>>=
链,因此将sequence
应用于无限列表可以'永远都没有完成。
如果你想使用无界的monadic序列,你需要显式的流控制 - 例如,以某种方式绑定到绑定链中的循环终止条件,这些简单的递归函数如mapM
和{ {1}}不提供 - 或一步一步的序列,如下所示:
sequence
...这样你就可以根据需要强制使用尽可能多的monad图层。
编辑:: 关于data Stream m a = Nil | Stream a (m (Stream m a))
,正在进行的是您传递splitRandom
计算,并使用种子Rand
进行评估,然后splitRandom
结果。如果没有return
,单个splitRandom
使用的种子必须来自无限列表排序的最终结果,因此它会挂起。使用额外的getRandom
,使用的种子只需要通过两个splitRandom
调用进行线程化,因此它可以工作。随机数的最终列表有效,因为你已经离开了splitRandom
monad并且没有任何东西取决于它的最终状态。
答案 1 :(得分:7)
这是尝试mapM return [1..]
未终止的证明。让我们暂时假设我们在Identity
monad中(该论点同样适用于任何其他monad):
mapM return [1..] -- initial expression
sequence (map return [1 ..]) -- unfold mapM
let k m m' = m >>= \x ->
m' >>= \xs ->
return (x : xs)
in foldr k (return []) (map return [1..]) -- unfold sequence
到目前为止一直很好......
-- unfold foldr
let k m m' = m >>= \x ->
m' >>= \xs ->
return (x : xs)
go [] = return []
go (y:ys) = k y (go ys)
in go (map return [1..])
-- unfold map so we have enough of a list to pattern-match go:
go (return 1 : map return [2..])
-- unfold go:
k (return 1) (go (map return [2..])
-- unfold k:
(return 1) >>= \x -> go (map return [2..]) >>= \xs -> return (x:xs)
回想一下身份monad中的return a = Identity a
和身份monad中的(Identity a) >>= f = f a
。继续:
-- unfold >>= :
(\x -> go (map return [2..]) >>= \xs -> return (x:xs)) 1
-- apply 1 to \x -> ... :
go (map return [2..]) >>= \xs -> return (1:xs)
-- unfold >>= :
(\xs -> return (1:xs)) (go (map return [2..]))
请注意,此时我们很乐意申请\xs
,但我们还不能!在我们有值申请之前,我们必须继续展开:
-- unfold map for go:
(\xs -> return (1:xs)) (go (return 2 : map return [3..]))
-- unfold go:
(\xs -> return (1:xs)) (k (return 2) (go (map return [3..])))
-- unfold k:
(\xs -> return (1:xs)) ((return 2) >>= \x2 ->
(go (map return [3..])) >>= \xs2 ->
return (x2:xs2))
-- unfold >>= :
(\xs -> return (1:xs)) ((\x2 -> (go (map return [3...])) >>= \xs2 ->
return (x2:xs2)) 2)
目前,我们仍无法申请\xs
,但我们可以申请\x2
。继续:
-- apply 2 to \x2 :
(\xs -> return (1:xs)) ((go (map return [3...])) >>= \xs2 ->
return (2:xs2))
-- unfold >>= :
(\xs -> return (1:xs)) (\xs2 -> return (2:xs2)) (go (map return [3..]))
现在我们已经达到了\xs
和 \xs2
都无法减少的程度!我们唯一的选择是:
-- unfold map for go, and so on...
(\xs -> return (1:xs))
(\xs2 -> return (2:xs2))
(go ((return 3) : (map return [4..])))
所以你可以看到,由于foldr
,我们正在建立一系列要应用的功能,从列表末尾开始,然后重新开始工作。因为在每一步输入列表都是无限的,这种展开永远不会终止,我们永远不会得到答案。
如果你看一下这个例子(从另一个StackOverflow线程借来,我目前找不到哪一个),这是有意义的。在以下monad列表中:
mebs = [Just 3, Just 4, Nothing]
我们希望sequence
能够抓住Nothing
并为整个事情返回失败:
sequence mebs = Nothing
但是,对于此列表:
mebs2 = [Just 3, Just 4]
我们希望序列给我们:
sequence mebs = Just [3, 4]
换句话说,sequence
有来查看整个monadic计算列表,将它们串在一起,并运行它们以便得出正确的答案。如果没有看到整个列表,sequence
就无法给出答案。
注意:此答案的先前版本断言foldr
从列表的后面开始计算,并且在无限列表上根本不起作用,但这是不正确的!如果传递给foldr
的运算符在其第二个参数上是惰性的,并使用类似列表的惰性数据构造函数生成输出,foldr
将很乐意使用无限列表。有关示例,请参阅foldr (\x xs -> (replicate x x) ++ xs) [] [1...]
。但我们的运营商k
并非如此。
答案 2 :(得分:7)
好的,这个问题可能非常简单。
q&lt; - mapM return [1 ..]
为什么这永远不会回来?
这不一定是真的。这取决于你所在的monad。
例如,使用标识monad,您可以懒惰地使用结果并终止正常:
newtype Identity a = Identity a
instance Monad Identity where
Identity x >>= k = k x
return = Identity
-- "foo" is the infinite list of all the positive integers
foo :: [Integer]
Identity foo = do
q <- mapM return [1..]
return q
main :: IO ()
main = print $ take 20 foo -- [1 .. 20]
答案 3 :(得分:3)
这个问题很好地展示了IO Monad和其他Monads之间的区别。在后台,mapM在所有列表元素之间构建一个带有绑定操作(&gt;&gt; =)的表达式,以将monadic表达式列表转换为列表的monadic表达式。现在,IO monad的不同之处在于Haskell的执行模型在IO Monad中的绑定期间执行表达式。这正是最终迫使(在一个纯粹的懒惰世界中)要执行的东西。
因此IO Monad在某种程度上是特殊的,它使用bind的序列范例来实际执行每一步的执行,这是我们的程序最终执行任何操作所做的。其他Monads是不同的。它们具有绑定运算符的其他含义,具体取决于Monad。 IO实际上是一个在绑定中执行事物的Monad,这就是为什么IO类型是“动作”的原因。
以下示例显示其他Monads不强制执行,例如Maybe monad。最后,这导致IO Monad中的mapM返回一个表达式,该表达式在执行时会在返回最终值之前执行每个单独的元素。
有关于此的好文章,从这里开始或搜索指称语义和Monads: 解决尴尬的小队:http://research.microsoft.com/en-us/um/people/simonpj/papers/marktoberdorf/mark.pdf
可能Monad的例子:
模块主要位置
fstMaybe :: [Int] - &gt;也许[Int] fstMaybe = mapM(\ x - &gt;如果x == 3那么没有别的只是x)
main = do 让r = fstMaybe [1 ..] 返回r
答案 4 :(得分:1)
让我们在更通用的背景下讨论这个问题。
正如其他答案所说,mapM
只是sequence
和map
的组合。所以问题是为什么sequence
在某些Monad
中是严格的。但是,这不仅限于Monads
,还包括Applicative
,因为我们sequenceA
在大多数情况下共享sequence
的相同实现。
现在看一下sequenceA
的<专用于列表>类型签名:
sequenceA :: Applicative f => [f a] -> f [a]
你会怎么做?您获得了一个列表,因此您希望在此列表中使用foldr
。
sequenceA = foldr f b where ...
--f :: f a -> f [a] -> f [a]
--b :: f [a]
由于f
是Applicative
,您知道b
是什么 - pure []
。但是什么是f
?
显然它是(:)
的升级版本:
(:) :: a -> [a] -> [a]
现在我们知道sequenceA
如何运作:
sequenceA = foldr f b where
f a b = (:) <$> a <*> b
b = pure []
或
sequenceA = foldr ((<*>) . fmap (:)) (pure [])
假设您获得了一个惰性列表(x:_|_)
。 sequenceA
的上述定义给出了
sequenceA (x:_|_) === (:) <$> x <*> foldr ((<*>) . fmap (:)) (pure []) _|_
=== (:) <$> x <*> _|_
现在我们看到问题已经减少,以考虑天气f <*> _|_
是否为_|_
。显然,如果f
是严格的,那么_|_
,但如果f不严格,要允许停止评估,我们要求<*>
本身不严格。
因此,应用仿函数的标准是sequenceA
停止运行
<*>
运算符是非严格的。一个简单的测试就是
const a <$> _|_ === _|_ ====> strict sequenceA
-- remember f <$> a === pure f <*> a
如果我们谈论的是Moand
,那么标准就是
_|_ >> const a === _|_ ===> strict sequence