Haskell 适用于简单IO示例的应用程序与一元风格
这里有两个非常简单的函数Haskell 适用于简单IO示例的应用程序与一元风格,haskell,monads,applicative,Haskell,Monads,Applicative,这里有两个非常简单的函数f和g {-# 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 <-
f
和g
{-# 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
{-#语言范围的TypeVariables}
模块测试在哪里
导入控制
f::IO()
f=do
y=返回。显示(*10) . 阅读)
写文件“out”y
f
中读取和*10
的文件是以应用程序风格编写的,带有pure
和()
。g
中读取和*10
的文件是以一元格式编写的,带有>=
。(我故意避免在g
中使用liftM
来强调下面的问题)
f
和g
之间的语义区别是什么?或者在这种情况下,这只是一种风格选择吗?show。(*10) . read
是一个“非单子”函数,我的意思是,它在IO单子中不起任何作用,从它的类型可以看出
>>=返回。
可以缩短为
`liftM`
但是
应始终等同于
`fmap`
fmap
既不需要monad类型类也不需要applicative类型类,它只需要functor类型类
现在我们将注意力转向应用程序版本,如下所示:
(<*>) (pure ...
()(纯。。。
相当于
,它只是fmap
因此,在这两种情况下,我们“实际上”只是在读写之间处理函子操作,尽管您以稍微不同的方式组合了函数(我们需要应用一个或多个“定律”将两个版本相互转换),语义是-或应该是-相同的。无论如何,它们肯定与IO单子相同。是的,选择纯粹是风格上的,但两者都可以整理成更地道的: 应用程序函数最好以运算符形式编写,且无点:
f :: IO ()
f = show . (*10) . read <$> readFile "data" >>= writeFile "out"
f::IO()
f=显示。(*10)。读取读取文件“数据”>>=写入文件“输出”
在一元风格中,如果你更全心全意而不是毫无意义地避免操作员,它看起来更整洁:
g :: IO ()
g = do
y <- readFile "data"
let x = show . (*10) . read $ y
writeFile "out" x
g::IO()
g=do
一元风格至少在我看来更具可读性:)你可以把()(pure(show(*10)))(readFile“data”)
写成show。(*10) . 读取读取文件“数据”
g :: IO ()
g = do
y <- readFile "data"
let x = show . (*10) . read $ y
writeFile "out" x