此代码来自此article
我已经能够遵循这一部分。
module Test where
type State = Int
data ST a = S (State -> (a, State))
apply :: ST a -> State -> (a,State)
apply (S f) x = f x
fresh = S (\n -> (n, n+1))
instance Monad ST where
-- return :: a -> ST a
return x = S (\s -> (x,s))
-- (>>=) :: ST a -> (a -> ST b) -> ST b
st >>= f = S (\s -> let (x,s') = apply st s in apply (f x) s')
data Tree a = Leaf a | Node (Tree a) (Tree a) deriving (Show)
mlabel :: Tree a -> ST (Tree (a,Int))
-- THIS IS THE PART I DON'T UNDERSTAND:
mlabel (Leaf x) = do n <- fresh
return (Leaf (x,n))
mlabel (Node l r) = do l' <- mlabel l
r' <- mlabel r
return (Node l' r')
label t = fst (apply (mlabel t) 0)
tree = Node (Node (Leaf 'a') (Leaf 'b')) (Leaf 'c')
label tree
产生:
Node (Node (Leaf ('a',0)) (Leaf ('b',1))) (Leaf ('c',2))
我可以看到>>=
运算符是'链接'返回monad(或类似的东西)函数的工具。
虽然我认为我理解这段代码,但我不明白这段代码是如何工作的。
具体来说是do n <- fresh
。我们还没有通过任何争论,对吧?在这种情况下n <- fresh
会产生什么?绝对不明白。也许它与currying有关?
答案 0 :(得分:8)
特别是n&lt; - 新鲜。我们还没有通过任何争论,对吧?
完全。我们正在编写一个论点,当我们做parse
之类的事情时,将传递给fresh
。一个很好的练习可以帮助您更清楚地了解正在发生的事情,首先使用明确的apply (mlabel someTree) 5
而不是do-notation编写mlabel
,然后替换(>>=)
和(>>=)
用return
实例说它们是什么。
答案 1 :(得分:5)
要认识到的关键是do
符号被转换为Monad
函数,所以
do n <- fresh
return (Leaf (x,n))
是
的缩写fresh >>= (\n ->
return (Leaf (x,n)) )
和
do l' <- mlabel l
r' <- mlabel r
return (Node l' r')
是
的缩写mlabel l >>= (\l' ->
mlabel r >>= (\r' ->
return (Node l' r') ))
这有望让您继续查明代码的含义,但是为了获得更多帮助,您应该阅读Monad的do
表示法。
答案 2 :(得分:4)
内联的monadic“pipelining”,您的代码变为
fresh state = (state, state + 1)
mlabel (Leaf x) state = -- do
let (n, state') = fresh state -- n <- fresh
in (Leaf (x,n), state') -- return (Leaf (x,n))
mlabel (Node l r) state = -- do
let (l', state') = mlabel l state -- l' <- mlabel l
in let (r', state'') = mlabel r state' -- r' <- mlabel r
in (Node l' r', state'') -- return (Node l' r')
main = let (result, state') = mlabel tree 0
in print result
{- Or with arrows,
mlabel (Leaf x) = Leaf . (x ,) &&& (+ 1)
mlabel (Node l r) = mlabel l >>> second (mlabel r)
>>> (\(a,(b,c)) -> (Node a b,c))
main = mlabel tree >>> fst >>> print $ 0
-}
或者在命令式伪代码中:
def state = unassigned
def fresh ():
tmp = state
state := state + 1 -- `fresh` alters the global var `state`
return tmp -- each time it is called
def mlabel (Leaf x): -- a language with pattern matching
n = fresh () -- global `state` is altered!
return (Leaf (x,n))
def mlabel (Node l r):
l' = mlabel l -- affects the global
r' = mlabel r -- assignable variable
return (Node l' r') -- `state`
def main:
state := 0 -- set to 0 before the calculation!
result = mlabel tree
print result
计算result
更改state
可分配的内容;它对应于Haskell的snd
元组中的(a, State)
字段。元组的fst
字段是新构造的树,在其叶子中携带数字和数据。
这些变体在功能上是等效的。
也许你已经听过关于monadic bind是“可编程分号”的口号。这里的含义很明确:它定义了“函数调用协议”,可以这么说,我们使用第一个返回值作为计算结果,第二个返回值作为更新状态,我们将其传递给下一个计算,所以它可以看到更新的状态。
这是状态传递编程风格(对于例如Prolog而言必不可少),使状态更改显式但必须手动负责传递正确的,更新状态。 Monads允许我们抽象出从一个计算到下一个计算的状态“布线”,所以它是为我们自动完成的,代价是必须以命令式的方式思考,并且让这个状态变得隐藏,再次隐含(比如状态变化隐含在命令式编程中,我们想在切换到函数式编程时首先避免这种情况......)。
所以状态monad所做的就是为我们维护这个隐藏状态,并在连续计算之间更新它。所以它毕竟不是 major 。