繁体   English   中英

Haskell:加入State Monad

[英]Haskell: Join on State Monad

如何正式计算/解释以下表达式?

runState (join (State $ \s -> (push 10,1:2:s))) [0,0,0]

我理解非正式的解释,它说:首先运行外部有状态计算,然后运行结果计算。

好吧,这对我来说很奇怪,因为如果我遵循join>>=定义,在我看来我必须从内部monad( push 10 )作为id的参数开始,然后执行... hmmmm ...好吧...我不确定...为了得到应该是什么结果:

((),[10,1,2,0,0,0])  

但是如何用正式定义来解释它:

instance Monad (State s) where  
    return x = State $ \s -> (x,s)  
    (State h) >>= f = State $ \s -> let (a, newState) = h s  
                                        (State g) = f a  
                                    in  g newState  

join :: Monad m => m (m a) -> m a
join n = n >>= id

同样,由于有一些“直觉” /视觉含义(与仅满足蒙纳德法律的形式化定义相反),很难理解国家Monad的绑定( >>= )的定义。 它具有不太正式和更直观的含义吗?

如果你擅长的类型, joinState s您可以:

join :: State s (State s a) -> State s a

因此,如果给定一个有状态计算并返回另一个有状态计算的结果,则join将它们合并为一个。

在您的问题中未提供push的定义,但我认为它看起来像:

push :: a -> State [a] ()
push x = modify (x:)

以及某些State类型

data State s a = State (s -> (a, s))

State sa的值是一个函数,给定类型为s的当前状态的值,该函数将返回一个包含类型a a的结果和新状态值的对。 因此

State $ \s -> (push 10,1:2:s)

具有类型State [Int] (State [Int] ()) (或Int以外的其他一些数字类型)。外部State函数作为结果返回另一个State计算,并更新状态以将值12推入状态。

State类型的join实现如下所示:

join :: State s (State s a) -> State s a
join outer = State $ \s ->
  let (inner, s') = runState outer s
  in  runState inner s'

因此它构造了一个新的有状态计算,该状态计算首先运行外部计算以返回包含内部计算和新状态的对。 然后以中间状态运行内部计算。

如果将示例插入此定义,则

outer = (State $ \s -> (push 10,1:2:s))
s = [0,0,0]
inner = push 10
s' = [1,2,0,0,0]

因此结果是runState (push 10) [1,2,0,0,0] ,即((),[10,1,2,0,0,0])

State的经典定义非常简单。

newtype State s a = State {runState :: s -> (a,s) }

State sa是一个“计算”(实际上只是一个函数),它采用s类型s某种东西(初始状态),并产生a类型a某种东西(结果)和s类型s某种东西(最终状态)。

您在问题中为>>=给出的定义使State sa成为“惰性状态转换器”。 这对于某些事情很有用,但是比严格的版本(它看起来像这样)更难理解,行为也不好:

m >>= f = State $ \s ->
  case runState m s of
    (x, s') -> runState (f x) s'

我消除了懒惰,还利用机会使用了记录选择器,而不是对State模式匹配。

这是在说什么 给定初始状态,我runState ms状态runState ms以获得结果x和新状态s' 我将f应用于x以获得状态转换器,然后以初始状态s'运行该转换器。

惰性版本仅在元组上使用惰性模式匹配。 这意味着函数f可以尝试生成状态转换器而无需检查其参数,并且转换器可以尝试运行而无需查看初始状态。 在某些情况下,您可以使用这种懒惰来打结递归结,实现有趣的功能(例如mapAccumR ,并在惰性增量流处理中使用状态,但是大多数时候您并不是真的想要/不需要它。

我认为Lee很好地解释了join作用。

您提到了join>>=的定义,因此,让我们尝试一下。

runState (join (State $ \s -> (push 10,1:2:s))) [0,0,0] = ?

定义又是

instance Monad (State s) where  
    -- return :: a -> State s a
    return x = State $ \s -> (x,s)  

所以对于x :: aState $ \\s -> (x,s) :: State sa ; (*)---->

    (State h) >>= f = State $ \s -> let (a, newState) = h s  
                                        (State g) = f a  
                                    in  g newState  

join m = m >>= id

runState :: State sa -> s -> (a, s) ,即它应该是(*)<----

runState (State g) s = gs 因此,遵循定义

runState (join (State $ \s -> (push 10,1:2:s))) [0,0,0]
 = runState (State g) [0,0,0] 
     where (State g) = join (State $ \s -> (push 10,1:2:s))
                     = (State $ \s -> (push 10,1:2:s)) >>= id
                    -- (State      h                 ) >>= f 
                     = State $ \s -> let (a, newState) = h s  
                                         (State g) = id a  
                                         h s = (push 10,1:2:s)
                                     in g newState  
                     = State $ \s -> let (a, newState) = (push 10,1:2:s)  
                                         (State g) = a
                                     in g newState  
                     = State $ \s -> let (State g) = push 10
                                     in g (1:2:s)

现在, push 10 :: State sa应该与State g匹配,其中g :: s -> (a, s) ; 它最有可能被定义为push 10 = State \\s-> ((),(10:) s) ; 所以我们有

                     = State $ \s -> let (State g) = State \s-> ((),(10:) s)
                                     in g (1:2:s)
                     = State $ \s -> let g s = ((),(10:) s)
                                     in g (1:2:s)
                     = State $ \s -> ((),(10:) (1:2:s))

 = runState (State $ \s -> ((),(10:) (1:2:s)) ) [0,0,0] 
 = (\s -> ((),(10:) (1:2:s))) [0,0,0]
 = ((), 10:1:2:[0,0,0]) 

因此,您可以看到push 10首先作为结果值生成的( (a, newState) = (push 10,1:2:s) ); 然后将其视为State sa类型的计算描述,因此最后运行(不是您想的那样首先运行)。

如Lee所述, join :: State s (State sa) -> State sa ; 这种类型的含义是, State s (State sa)是将State sa作为其结果值的计算,即push 10 只有掌握了它,我们才能运行它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM