6

如何在 IO 函数中使用纯函数?:-/

例如:我正在读取一个文件(IO 函数),我想通过使用具有引用透明性的纯函数来解析它的上下文,一个字符串。

似乎这样的世界,纯函数和 IO 函数,是分开的。我怎么可能桥接他们?

4

4 回答 4

12

最简单的方法是使用fmap,它具有以下类型:

fmap :: (Functor f) => (a -> b) -> f a -> f b

IOimplements Functor,这意味着我们可以通过替换IOforf来专门化上述类型:

fmap :: (a -> b) -> IO a -> IO b

换句话说,我们采用一些将as 转换为bs 的函数,并使用它来更改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

于 2013-02-17T04:49:00.163 回答
2

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 
于 2013-02-17T00:51:39.903 回答
2

亚历克斯霍斯曼帮助了我。他说:

“也许我误会了,但这听起来很简单?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)
}
于 2013-02-17T00:45:36.940 回答
2

实际答案如下:

main = do
  val <- return (purefunc ...arguments...)
  ...more..actions...

return将其包装在适当的 monad 中,以便do可以将其分配给val.

于 2015-06-11T20:00:45.443 回答