您无法编写它们,至少不能单独使用(.)
编写它们。不过,您可以使用fmap
(或其操作符版本<$>
)
lines <$> readFile "quux.txt" -- Produces IO [String],not [String]
用一种合成表达这种方式的一种方法是,首先从a -> m b
创建一个Kleisli箭头(某单子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
现在,您可以使用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"
将其与>>=
运算符进行比较,该运算符要求您在将结果提供给readFile
之前将文件名提供给return . lines
:
-- m >>= return . f === fmap f m === f <$> m
readFile "quux.txt" >>= kleisliLines
>=>
很自然,如果您已经想到了>=
方面的管道;如果您想要某种保留.
顺序的东西,请使用<=<
(也在Control.Monad
中定义为(<=<) = flip (>=>)
;操作数将被颠倒)。
(kleisliLines <=< readFile) "quux.txt"
,
到目前为止,给出的其他答案是使lines
生成一个空的单子上下文,然后使用单子组合(<=<
)与readFile
进行组合。但是您也可以朝另一个方向前进:提起lines
以通过一元论证进行运算,然后使用普通的合成将其与readFile
结合起来:
(fmap lines . readFile) "quux.txt"
当然,如果您要立即将其应用于参数,则只需编写即可
lines <$> readFile "quux.txt"
本文链接:https://www.f2er.com/3144033.html