Haskell 是哈斯克尔';我不懒吗?

Haskell 是哈斯克尔';我不懒吗?,haskell,monads,lazy-evaluation,Haskell,Monads,Lazy Evaluation,更新:好的,这个问题可能非常简单 q <- mapM return [1..] q兰德g(双倍,[Double]) f=do 嗯,有懒惰的,然后是懒惰的mapM确实很懒,因为它做的工作不多。但是,请查看类型签名: mapM :: (Monad m) => (a -> m b) -> [a] -> m [b] 想想这意味着什么:你给它一个函数a->mb和一堆as。一个常规的map可以将它们变成一堆mbs,但不是mb。将bs组合成单个[b]而不受单子影响的唯一方法是

更新:好的,这个问题可能非常简单

q <- mapM return [1..]
q兰德g(双倍,[Double])
f=do

嗯,有懒惰的,然后是懒惰的
mapM确实很懒,因为它做的工作不多。但是,请查看类型签名:

mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
想想这意味着什么:你给它一个函数
a->mb
和一堆
a
s。一个常规的
map
可以将它们变成一堆
mb
s,但不是
mb
。将
b
s组合成单个
[b]
而不受单子影响的唯一方法是使用
>=
mb
s排序在一起以构建列表

事实上,
mapM
恰好等同于
序列。地图


一般来说,对于任何一元表达式,如果使用该值,则必须强制执行指向该表达式的整个
>=
s链,因此将
序列
应用于无限列表永远无法完成

如果你想处理一个无界的一元序列,你要么需要显式的流控制——例如,一个循环终止条件以某种方式烘焙到绑定链中,这是像
mapM
sequence
这样的简单递归函数不提供的——要么需要一个循序渐进的序列,类似这样:

data Stream m a = Nil | Stream a (m (Stream m a))
…这样您只需强制执行所需数量的monad层


Edit::关于
splitRandom
,这里发生的事情是,您正在向它传递一个
Rand
计算,用种子
splitRandom
计算,然后
返回结果。没有
splitRandom
,单个
getRandom
使用的种子必须来自对无限列表排序的最终结果,因此它挂起。使用额外的
splitRandom
,使用的种子只需要通过两个
splitRandom
调用执行线程,因此它可以工作。最后一个随机数列表之所以有效,是因为您将
Rand
单子留在了这一点上,没有任何东西取决于它的最终状态。

这里尝试证明
mapM return[1..]
没有终止。现在让我们假设我们处于
身份
单子中(该参数同样适用于任何其他单子):

到目前为止还不错

-- 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)
回想一下,在标识单子中返回a=Identity a
,在标识单子中返回f=fa
。继续:

-- 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..]))
-- 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
,但我们还不能!相反,我们必须继续展开,直到我们有了一个应用的价值:

-- 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
。继续:

-- 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..]))
-- 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线程,我现在找不到哪个)。在以下单子列表中:

mebs = [Just 3, Just 4, Nothing]
我们希望
sequence
捕获
Nothing
并返回整个事件的失败:

sequence mebs = Nothing
但是,对于此列表:

mebs2 = [Just 3, Just 4]
我们希望该序列能够为我们提供:

sequence mebs = Just [3, 4]
换句话说,
sequence
必须查看一元计算的整个列表,将它们串在一起,然后运行它们,才能得出正确的答案。如果不看完整的列表,
sequence
无法给出答案

注意:这个答案的前一个版本断言
foldr
从列表的后面开始计算,在无限列表上根本不起作用,但这是错误的!如果传递给
foldr
的运算符在其第二个参数上是惰性的,并使用惰性数据构造函数(如列表)生成输出,
foldr
将愉快地使用无限列表。有关示例,请参见
foldr(\x xs->(replicate x x)++xs)[[1…]
。但我们的操作员
k
并非如此

好的,这个问题可能变得非常简单

q>=k=kx 返回=标识 --“foo”是所有正整数的无限列表 foo::[整数] 标识foo=do
q让我们在更一般的上下文中讨论这个问题

正如其他答案所说,
mapM
只是
序列
map
的组合。所以问题是为什么在某些
Monad
s中
sequence
是严格的。但是,这不仅限于
单子
,还包括
应用程序
s,因为我们有
sequenceA
,它们在大多数情况下共享相同的
序列
实现

现在看一下
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
是一个
应用程序
,您知道
b
可以是一个
纯[]
。但是什么是
f
? 显然,它是
(:)
的升级版本:

现在我们知道了
sequenceA
的工作原理:

sequenceA = foldr f b where
  f a b = (:) <$> a <*> b
  b = pure []
<> P> >现在我们看到这个问题减少到考虑天气>代码> F > <代码> >代码> < <代码> >。显然,如果
f
是严格的,这就是
|
,但是如果f不是严格的,为了允许停止评估,我们要求
本身是非严格的

因此,应用函子的
sequenceA
停止的条件是
操作员必须是非stri操作员
(:) :: a -> [a] -> [a]
sequenceA = foldr f b where
  f a b = (:) <$> a <*> b
  b = pure []
sequenceA = foldr ((<*>) . fmap (:)) (pure [])
sequenceA (x:_|_) === (:) <$> x <*> foldr ((<*>) . fmap (:)) (pure []) _|_
                  === (:) <$> x <*> _|_
const a <$> _|_ === _|_      ====> strict sequenceA
-- remember f <$> a === pure f <*> a
_|_ >> const a === _|_ ===> strict sequence