Ger*_*ely 1 io monads haskell function-composition io-monad
linesHaskell中的函数将字符串的行分成字符串列表:
lines :: String -> [String]
Run Code Online (Sandbox Code Playgroud)
该readFile函数将文件读取为字符串:
readFile :: FilePath -> IO String
Run Code Online (Sandbox Code Playgroud)
尝试组合这些函数以获取文件中的行列表会导致类型错误:
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"
Run Code Online (Sandbox Code Playgroud)
我该怎么做单子戏?
您无法编写它们,至少不能(.)单独使用它们。不过,您可以使用fmap(或其操作符版本<$>):
lines <$> readFile "quux.txt" -- Produces IO [String], not [String]
Run Code Online (Sandbox Code Playgroud)
用一种合成表达这种方式的一种方法是首先从以下项中创建一个Kleisli箭头(a -> m b某些monad 的类型函数m)lines:
-- 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
Run Code Online (Sandbox Code Playgroud)
现在,您可以使用Kleisli合成运算符>=>组合readFile(本身就是Kleisli箭头)和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"
Run Code Online (Sandbox Code Playgroud)
将此与>>=操作员进行比较,该操作员需要在提供readFile结果之前将文件名提供给return . lines:
-- m >>= return . f === fmap f m === f <$> m
readFile "quux.txt" >>= kleisliLines
Run Code Online (Sandbox Code Playgroud)
>=>如果您已经在考虑管道的话,这是很自然的>=;如果您想要某种保留,顺序的东西.,请使用<=<(也在中定义Control.Monad为(<=<) = flip (>=>),操作数只是颠倒过来)。
(kleisliLines <=< readFile) "quux.txt"
Run Code Online (Sandbox Code Playgroud)