适用于简单IO示例的适用于monadic风格

时间:2013-11-30 14:09:18

标签: haskell monads applicative

这是两个非常简单的函数fg

{-# LANGUAGE ScopedTypeVariables #-}

module Test where

import Control.Applicative

f :: IO ()
f = do
    y <- (<*>) (pure (show . (*10))) (read <$> readFile "data")
    writeFile "out" y

g :: IO ()
g = do
    y <- (readFile "data" >>= return . show . (*10) . read)
    writeFile "out" y

阅读的文件和*10中的f是以pure(<*>)的应用样式编写的。读取的文件和*10中的g是以monadic样式编写的,>>=。 (我故意避免在liftM中使用g来强调下面的问题。)

fg之间的语义差异是什么?或者在这种情况下,它只是一种风格选择吗?

2 个答案:

答案 0 :(得分:8)

show . (*10) . read是一个“非monadic”函数 - 我的意思是,它在IO monad中没有任何作用,正如你可以从它的类型中看到的那样。

>>= return .可以缩短为

`liftM`

但是

`liftM`

应始终等同于

`fmap`

fmap既不需要monad类型类也不需要应用类型类,它只需要仿函数类型类。

现在把注意力转向应用版本,这个:

(<*>) (pure ...

相当于<$>,只是fmap

因此,在这两种情况下,我们“真的”只是在编写器之间进行仿函数操作,尽管你以稍微不同的方式组合了函数(我们需要应用一个或多个“法则”)将两个版本互相翻译),语义是 - 或应该 - 相同。无论如何,他们肯定是IO monad。

答案 1 :(得分:1)

是的,选择纯粹是风格,但两者都可以整理成更具惯用性:

应用函数最好写成运算符,并且无点:

f :: IO ()
f = show . (*10) . read <$> readFile "data" >>= writeFile "out"

在monadic风格中,如果你更全心全意地不是无点的,那么看起来更整洁,避免操作员:

g :: IO ()
g = do 
    y <- readFile "data" 
    let x = show . (*10) . read $ y
    writeFile "out" x