如何正式计算/解释以下表达式?
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法律)。它是否具有不太正式和更直观的含义?
答案 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
计算结果,并更新状态以使其具有值1
和2
被推到了它上面。
此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 :: a
,State $ \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
;我们只有在掌握它之后才能运行它。