- 论坛徽章:
- 2
|
回复 36# fergon
monad…… 难得其实是那个名字
高级使用,monad transform神马的…… 最近被卡在array上…… 没有研究……
就只看一些常用的monad instance的话,其实monad class的两个最重要成员的签名已经可以说明它是做啥的了……
- return :: Monad m => a -> m a
- (>>=) :: Monad m => m a -> (a -> m b) -> m b
复制代码 一旦用return将类型为a的x提升到类型m a的附带有context的x'中(无论是自己使用return附带的,还是从别处获得的带有context的value),就再也没法通过Monad的method重新取回原值x。
Monad的另外两个method也是不行的:
- (>>) :: m a -> m b -> m b
- fail :: String -> m a
复制代码 唯一能做的,就是通过bind(>>=)。通过它的第2个参数 —— 一个函数 —— 第1个参数取回,并进行一些计算。
并且计算的结果不能是"裸"的值,是有限制的: >>= 的第1参数是 m a (m是type constructor) , 那么第2参数的返回类型(也是>>=的返回类型)必须是同一个type constructor m。
用类型系统保证不能让普通的类型a从Monad中逃逸(escape)出去,至少是没法通过Monad methods完成。
于是具体的Monad instance就了解用户只能通过的>>=来获取其内容,于是就可以在其中动一些手脚了……
于是这货就可以用来来解决IO了……
IO(ghc实现里)会附带一些primitive,以约束>>=的求值的严格性、顺序。
比如:用户没法获取这个String,除非通过>>=:
- getLine >>= (\s -> return (map toUpper s)) :: IO [Char]
复制代码 或者写成point free:
- getLine >>= return . map toUpper :: IO [Char]
复制代码 注意return, 再次将结果提升到IO monad之中,避免该值被直接获取。 而如果不用return, map toUpper 就不符合>>=的签名。
为了获取大写的string,唯一能做的是再次>>=,比如这次就输出了:
- putStr :: String -> IO ()
- getLine >>= return . map toUpper >>= putStr :: IO ()
复制代码 又或者将那个return省掉,直接:
- getLine >>= putStr . map toUpper :: IO ()
复制代码 最终,haskell程序就是要这样组合出一个main :: IO a, 比如:
- main = getLine >>= putStr . map toUpper
复制代码 程序就是对main求值,得到a,并舍弃其结果(仅为了副作用)。
整个过程中,IO a -> a都是被控制在:
1. >>= 的第2个参数里
2. 对main的求值
于是IO实现>>=时就可以保证 getLine/putStr一定会发生且仅一次,并且getLine一定在putStr之前。(并且对main稍微再动点手脚……)
当然,并不是真的没法从m a中逃逸得到a。通过Monad methods不行,但具体的Monad instance是可以的。
IO的话,可以pattern match(或者case)它的data constructor:
- newtype IO a
- = GHC.Types.IO (GHC.Prim.State# GHC.Prim.RealWorld
- -> (# GHC.Prim.State# GHC.Prim.RealWorld, a #))
复制代码 但这应该被认为是private的,就像很多支持private的语言里即使有获取private的方法也应该当作不知道。
另外还有一套公开的:
- System.IO.Unsafe.unsafePerformIO :: IO a -> a
- -- 这个module里还有一些
复制代码 但这时候程序员就要自己保证如果求值被省略(甚至多次)与改变顺序也会得到希望的结果。
另外的一些具体的monad instance的逃逸就是公开的了…… runST, (case a of Just x -> ...; Nothing -> ...), xs !! idx 等等……
而且它们对>>=的含义也不一定是用于限制求值顺序了。
比如Maybe,只要其中有一个是Nothing,整个bind链的结果就是Nothing。
list就用作这样的事情:
- "ab" >>= \ x -> [toLower x,toUpper x] -- > "aAbB"
- concat [ [toLower x, toUpper x] | x <- "ab" ] -- > "aAbB"
复制代码 总之,Monad class就只描述了那2个(把fail算上就是3个吧)member。
具体的Monad instance能干什么…… 就可以发挥你的想象了…… 你让它做什么,它就是什么…… 当然,有个monad law的……
附带一点材料……
我是从 http://learnyouahaskell.com/a-fistful-of-monads 开始,感觉终于可以用这货了……
这书前面部分有点无聊(类容简单、还配一些很花哨的图……) 所以之前的感觉是这书不咋嘀……
但耐心看过一次后,尤其要看前一章就有悟了的感觉。
|
|