2

这里有两个非常简单的函数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的应用样式写入。读取和写入的文件以monadic 样式写入,带有. (我故意避免使用in来强调下面的问题)。pure(<*>)*10g>>=liftMg

f和之间的语义区别是什么g?或者在这种情况下,它只是一种风格选择吗?

4

2 回答 2

8

show . (*10) . read是一个“非单子”函数——我的意思是,它在 IO 单子中不做任何事情,从它的类型可以看出。

>>= return .可以缩短为

`liftM`

`liftM`

应该总是等价于

`fmap`

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

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

(<*>) (pure ...

相当于<$>,也就是fmap

因此,在这两种情况下,我们“真的”只是使用函子操作,在读取和写入之间,尽管您以稍微不同的方式组合了这些函数(我们需要应用一个或多个“定律”来翻译这两者版本),语义是 - 或应该是 - 相同的。无论如何,他们当然是与 IO monad 在一起的。

于 2013-11-30T14:42:17.273 回答
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
于 2013-12-01T09:04:05.673 回答