老师:Monad想到什么呢? 同学:毛茸茸的猫猫!
好同学们,请默念“Warm and Fuzzy”100遍~
。。
。。
。。
。
。
。
。
。
。
。
。
。
。。
。。
100遍了没。。。好,我们先看看State猫猫的类型吧 ^_^
newtype State s a = State { runState :: s -> (a, s) }
里面有个难看的record syntax,咱们无视之,改成:
data State g v = State (g -> (v, g))
就是一个lambda,外加一二元组:v表示猫猫正经计算中的值,g就表示猫猫的状态啦(就一个全局变量嘛)~那个lambda的参数g也一样。相应的猫猫Instance也就是如下:
instance Monad (State g) where
return v =
State $ \g -> (v, g)
(>>=) (State m) f =
State $ \g ->
let (v, g') = m g
(State m') = f v
in m' g'
还是一环套一环,函数编程没有变量,那就向下传递的时候改变它的值就行了。再加两个小喽骡:
get_g = State $ \g -> (g, g)
set_g v = State $ \_ -> (v, v)
全局变量的值不也在那个lambda的参数里么,get_g它设成计算中的值,这样在do-notation中就可以g <- get_g了。set_g则是把它的参数设到g里,而原先的g无视扔掉即可。
加个函数试验下:
run (State m) i = m i
add1 :: State Int Int
add1 = do {
g <- get_g;
set_g $ g + 1;
}
add3 = do {
add1;
add1;
add1;
}
进ghci输入run add3 1可得(4,4),可见那个“全局变量”就这么变化了。
回头再想想它的类型,一个二元组不就够了嘛,为什么要套那层lambda呢?试下就知道
data State g v = State (g, v)
instance Monad (State g) where
(>>=) (State (g, v)) f = (g, f v)
看着不错,接着来:
return v = ...
发现问题了没,return我们实现不了...搞不到原先g的值,链子就断在了这一环。解决方法就是把它推后,不是没有g么,于是放到参数里等着别人(>>=)给,这算不算惰性呢? :)
ps:明白了State是怎么回事,附一山寨parsec ^_^ http://github.com/Fleurer/FParser