Haskell - Monad绑定评估顺序

我在计算/绑定操作符如何将下列状态单元绑定在一起时遇到了一些麻烦:

pop :: State [Int] Int
pop = do
        (x:xs) <- get
        put xs
        return x

push :: Int -> State [Int] ()
push x = do 
            xs <- get
            put (x:xs)

doStuff :: State [Int] ()
doStuff = do
            pop
            x <- pop
            push 5
            push x

采取doStuff ,可以解决以下问题:

pop >>= (_ -> pop >>= (x -> push 5 >>= (_ -> push x)))

当评估这条线时,绑定实际发生的顺序是什么? 因为为了实际绑定,Haskell需要从>>=运算符右侧的函数中获得状态monad(即,需要首先充分评估函数右操作数),我会认为会发生以下情况:

  • s1 = push 5 >>= (_ -> push x)
  • s2 = pop >>= (x -> s1)
  • s3 = pop >>= (_ -> s2)
  • 这是考虑它的正确方法吗? 我觉得我很好地理解单子,但是我最大的问题在于真正想象“幕后”发生了什么,以及数据如何流动,可以这么说。 该do记号给出了我处理了一堆顺序操作的,而事实上,有一大堆的嵌套和关闭的错觉。

    我感觉有点像我在这里过度思考,并因此导致我自己更加困惑。


    从...开始

    pop >>= (_ -> pop >>= (x -> push 5 >>= (_ -> push x)))
    

    可以内联一些功能(以更好地显示发生的情况)。 我会从(>>=) ,假装State没有被定义为一个变换器或newtype,以保持简单。

    type State s a = s -> (a, s)
    m >>= k =  s -> let (a, s') = m s in k a s'
    
     s -> let (a, s') = pop s in
    ( _ -> pop >>= ( x -> push 5 >>= ( _ -> push x))) a s'
    
     s -> let (_, s') = pop s in
    (pop >>= ( x -> push 5 >>= ( _ -> push x))) s'
    
     s -> let (_, s') = pop s in
    let (a, s'') = pop s' in
    ( x -> push 5 >>= ( _ -> push x)) a s''
    
     s -> let (_, s') = pop s in
    let (a, s'') = pop s' in
    (push 5 >>= ( _ -> push a)) s''
    
     s -> let (_, s') = pop s in
    let (a, s'') = pop s' in
    let (b, s''') = push 5 s'' in
    ( _ -> push a)) b s'''
    
    
     s -> let (_, s') = pop s in
    let (a, s'') = pop s' in
    let (_, s''') = push 5 s'' in
    push a s'''
    

    这是考虑它的正确方法吗?

    没有。

    首先:虽然在IO monad中想到“首先发生这种情况,比这个更好,然后我们评估这个键盘输入......”显然是正确的,但并非所有monad都是如此。 例如,在monad列表中,这没有任何意义。 一般来说,根本不可能在Haskell中计算特定的顺序,它不是定义的行为。

    然而,在monad中考虑一个计算顺序总是可能的,而且经常是有帮助的,而这个顺序实际上do符号建议的顺序。 所以,大多数情况下,思考脱俗的表情并不真切。 但是,如果你想要迈出这一步,以下是我该怎么做:

  • pop >>= _ -> THUNK1

  • THUNK1≡> pop >>= x -> THUNK2

  • { Closure{x} } THUNK2≡> push 5 >>= _ -> THUNK3

  • { Closure{x} } THUNK3≡> push x

  • 这当然是荒诞比较难看,但是他说,几乎一样的糖do表达。


    当评估这条线时,绑定实际发生的顺序是什么?

    这里没有什么特别的“绑定”。 desugared表达式的评估方式与其他表达式完全相同(惰性),具体取决于您正在使用的特定monad的实现(>>=)

    如果我们谈论的是使用类似runState ,给予类似的表达foo >>= (x -> bar) ,最外层的表达应用(>>=)但我们正在努力解开一个newtype ,然后应用里面的函数,所以(>>=)被强制执行,就像函数一样。

    如果我们考虑列表monad,则(>>=)concatMap 。 给定一个表达式,如[foo1, foo2] >>= (x -> [bar1, bar2, x] >>= (y -> [baz, y])) ,结果显然不会使用take 5完全计算所有绑定。


    也就是说,这里有一条重要的规则:无论在什么程度上评估x >>= f强迫x的评估,在一个像desugared do块这样的大表达式中,强制将以明显的“顺序”顺​​序发生,出于同样的原因“顺序错觉”是可能的。

    链接地址: http://www.djcxy.com/p/42947.html

    上一篇: Haskell — Monad binding evaluation order

    下一篇: Haskell: monadic takeWhile?