哈斯克尔:加入国家蒙纳德

时间:2016-02-11 20:13:14

标签: haskell

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

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

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

嗯,这对我来说很奇怪,因为如果我遵循join>>=定义,我觉得我必须从内部monad开始({{1} })作为push 10的参数,然后做......嗯......好吧......我不确定是什么......为了获得所谓的结果:< / p>

id

然而如何通过正式定义来解释它:

((),[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  

另外,State Monad的绑定(join :: Monad m => m (m a) -> m a join n = n >>= id )的定义很难掌握,因为它具有一些直观的&#34; /视觉意义(而不仅仅是一个正式的定义)这将满足Monad法律)。它是否具有不太正式和更直观的含义?

3 个答案:

答案 0 :(得分:3)

如果您专门针对join State 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 s a是一个函数,给定类型s的当前状态的值,返回包含类型a的结果和新状态值的对。因此

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

具有类型State [Int] (State [Int] ())(或Int以外的其他一些数字类型。外部State函数返回另一个State计算结果,并更新状态以使其具有值12被推到了它上面。

join类型的State实现如下:

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])

的结果

答案 1 :(得分:3)

State的经典定义非常简单。

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

State s a是&#34;计算&#34; (实际上只是一个函数),它采用s类型(初始状态)并生成类型a(结果)和类型s(最终状态)的类型。

您在>>=问题中提供的定义会使State s a成为一个&#34;懒惰状态转换器&#34;。这对某些事情很有用,但比严格版本更难以理解和表现不佳,如下所示:

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

我已删除了懒惰,并借此机会使用记录选择器而不是State上的模式匹配。

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

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

李先生很清楚地解释了join的作用。

答案 2 :(得分:0)

您提到了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 s a; (*)----&gt;

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

join m = m >>= id

runState :: State s a -> s -> (a, s),即它应该是(*)&lt; ----

runState (State g) s = g s。所以,遵循我们的定义

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 s a应该与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 s a的计算描述,因此运行 last (不是第一个,如你所想)。

正如李所描述的,join :: State s (State s a) -> State s a;这种类型的含义是,类型State s (State s a)的计算是生成State s a作为其结果值的计算,即push 10;我们只有在掌握它之后才能运行它。