Haskell IO单子中的函数组合

Haskell IO单子中的函数组合,haskell,io,monads,function-composition,io-monad,Haskell,Io,Monads,Function Composition,Io Monad,Haskell中的lines函数将字符串的行分隔为一个字符串列表: lines :: String -> [String] readFile函数将文件读入字符串: readFile :: FilePath -> IO String 尝试组合这些函数以获取文件中的行列表会导致类型错误: Prelude> (lines . readFile) "quux.txt" <interactive>:26:10: error: • Couldn't match ty

Haskell中的
lines
函数将字符串的行分隔为一个字符串列表:

lines :: String -> [String]
readFile
函数将文件读入字符串:

readFile :: FilePath -> IO String
尝试组合这些函数以获取文件中的行列表会导致类型错误:

Prelude> (lines . readFile) "quux.txt"
<interactive>:26:10: error:
    • Couldn't match type ‘IO String’ with ‘[Char]’
      Expected type: FilePath -> String
        Actual type: FilePath -> IO String
    • In the second argument of ‘(.)’, namely ‘readFile’
      In the expression: lines . readFile
      In the expression: (lines . readFile) "quux.txt"
Prelude>(lines.readFile)“qux.txt”
:26:10:错误:
•无法将类型“IO字符串”与“[Char]”匹配
所需类型:文件路径->字符串
实际类型:文件路径->IO字符串
•在“(.”的第二个参数中,即“readFile”
在表达式中:行。读取文件
在表达式中:(lines.readFile)“qux.txt”

我怎么才能在这里玩单子的把戏呢?

你不能单独编写它们,至少不能单独使用
()
。您可以使用
fmap
(或其操作员版本
),但:

现在,您可以使用Kleisli合成运算符
=>
组合
读取文件
(本身是Kleisli箭头)和

-- return . lines itself has type Monad m => String -> m [String]
-- but for our use case we can restrict the type to the monad
-- we are actually interested in.
kleisliLines :: String -> IO [String]
kleisliLines = return . lines
import Control.Monad  -- where (>=>) is defined

-- (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
-- Here, m ~ IO
--       a -> FilePath
--       b -> String
--       c -> [String]
(readFile >=> kleisliLines) "quux.txt"
-- m >>= return . f === fmap f m === f <$> m
readFile "quux.txt" >>= kleisliLines
将其与
>=
运算符进行比较,后者要求您在将结果馈送到
返回之前向
readFile
提供文件名。行

-- return . lines itself has type Monad m => String -> m [String]
-- but for our use case we can restrict the type to the monad
-- we are actually interested in.
kleisliLines :: String -> IO [String]
kleisliLines = return . lines
import Control.Monad  -- where (>=>) is defined

-- (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
-- Here, m ~ IO
--       a -> FilePath
--       b -> String
--       c -> [String]
(readFile >=> kleisliLines) "quux.txt"
-- m >>= return . f === fmap f m === f <$> m
readFile "quux.txt" >>= kleisliLines

到目前为止给出的其他答案是使
产生一个空的一元上下文,然后使用一元合成(
合成很好,但是使用
do
块没有什么丢脸的,比如
do内容我使用do符号,然后在我想清理代码时进行合成,如果它能提高清晰度的话。
lines <$> readFile "quux.txt"