如何在 IO 函数中使用纯函数?:-/
例如:我正在读取一个文件(IO 函数),我想通过使用具有引用透明性的纯函数来解析它的上下文,一个字符串。
似乎这样的世界,纯函数和 IO 函数,是分开的。我怎么可能桥接他们?
如何在 IO 函数中使用纯函数?:-/
例如:我正在读取一个文件(IO 函数),我想通过使用具有引用透明性的纯函数来解析它的上下文,一个字符串。
似乎这样的世界,纯函数和 IO 函数,是分开的。我怎么可能桥接他们?
最简单的方法是使用fmap
,它具有以下类型:
fmap :: (Functor f) => (a -> b) -> f a -> f b
IO
implements Functor
,这意味着我们可以通过替换IO
forf
来专门化上述类型:
fmap :: (a -> b) -> IO a -> IO b
换句话说,我们采用一些将a
s 转换为b
s 的函数,并使用它来更改IO
动作的结果。例如:
getLine :: IO String
>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST
那里刚刚发生了什么?好吧,map toUpper
有类型:
map toUpper :: String -> String
它接受 aString
作为参数,并返回 aString
作为结果。具体来说,它将整个字符串大写。
现在,让我们看一下 的类型fmap (map toUpper)
:
fmap (map toUpper) :: IO String -> IO String
我们已经升级了我们的函数来处理IO
值。它将IO
操作的结果转换为返回大写字符串。
我们也可以使用do
符号来实现这一点,以:
getUpperCase :: IO String
getUpperCase = do
str <- getLine
return (map toUpper str)
>>> getUpperCase
Test<Enter>
TEST
事实证明,每个 monad 都具有以下属性:
fmap f m = do
x <- m
return (f x)
换句话说,如果任何类型实现Monad
了 ,那么它也应该总是能够实现Functor
,使用上面的定义。事实上,我们总是可以使用 的liftM
作为默认实现fmap
:
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
x <- m
return (f x)
liftM
与 相同fmap
,除了专门用于 monad,它不像函子那样通用。
所以如果你想转换一个IO
动作的结果,你可以使用:
fmap
,liftM
, 或者do
符号这真的取决于你喜欢哪一个。我个人推荐fmap
。
You can also consider liftM function from Control.Monad.
A little example to help you (run it into ghci as you are under the IO Monad)
$ import Control.Monad -- to emerge liftM
$ import Data.Char -- to emerge toUpper
$ :t map to Upper -- A pure function
map toUpper :: [Char] -> [Char]
$ :t liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
$ liftM (map toUpper) getLine
亚历克斯霍斯曼帮助了我。他说:
“也许我误会了,但这听起来很简单?do {x <- ioFunc; return (pureFunc x)}”
然后我解决了我的问题:
import System.IO
import Data.List
getFirstPart line Nothing = line
getFirstPart line (Just index) = fst $ splitAt index line
eliminateComment line =
getFirstPart line $ elemIndex ';' line
eliminateCarriageReturn line =
getFirstPart line $ elemIndex '\r' line
eliminateEntersAndComments :: String -> String
eliminateEntersAndComments text =
concat $ map mapFunction $ lines text
where
mapFunction = (++ " ") . eliminateCarriageReturn . eliminateComment
main = do {
contents <- readFile "../DWR-operators.txt";
return (eliminateEntersAndComments contents)
}
实际答案如下:
main = do
val <- return (purefunc ...arguments...)
...more..actions...
return
将其包装在适当的 monad 中,以便do
可以将其分配给val
.