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

问题描述:

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

How can I use pure functions inside IO functions? :-/

例如:我正在读取文件(IO函数),并且我想通过使用具有参照透明性的纯函数来解析其上下文(字符串).

For example: I'm reading a file (IO function) and I want to parse its context, a string, by using a pure function with referential transparency.

似乎这些世界,纯功能和IO功能是分开的.我怎么可能桥接它们?

It seems such worlds, pure functions and IO functions, are separated. How can I possibly bridge them?

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

The simplest way is to use fmap, which has the following type:

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

IO实现Functor,这意味着我们可以通过用IO代替f来获得上述特殊类型:

IO implements Functor, which means that we can specialize the above type by substituting IO for f to get:

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

换句话说,我们采用了一些将a s转换为b s的函数,并使用该函数来更改IO动作的结果.例如:

In other words, we take some function that converts as to bs, and use that to change the result of an IO action. For example:

getLine :: IO String

>>> getLine
Test<Enter>
Test
>>> fmap (map toUpper) getLine
Test<Enter>
TEST

那里发生了什么?好吧,map toUpper具有类型:

What just happened there? Well, map toUpper has type:

map toUpper :: String -> String

String作为参数,并返回String作为结果.具体来说,它将整个字符串都大写.

It takes a String as an argument, and returns a String as a result. Specifically, it uppercases the entire string.

现在,让我们看一下fmap (map toUpper)的类型:

Now, let's look at the type of fmap (map toUpper):

fmap (map toUpper) :: IO String -> IO String

我们已经升级了功能,可以使用IO值.它将转换IO操作的结果以返回大写的字符串.

We've upgraded our function to work on IO values. It transforms the result of an IO action to return an upper-cased string.

我们还可以使用do表示法来实现此目的,

We can also implement this using do notation, to:

getUpperCase :: IO String
getUpperCase = do
    str <- getLine
    return (map toUpper str)

>>> getUpperCase
Test<Enter>
TEST

事实证明,每个monad具有以下属性:

It turns out that every monad has the following property:

fmap f m = do
    x <- m
    return (f x)

换句话说,如果任何类型实现Monad,那么它也应该始终能够使用上述定义来实现Functor.实际上,我们始终可以将liftM用作fmap的默认实现:

In other words, if any type implements Monad, then it should always be able to implement Functor, too, using the above definition. In fact, we can always use the liftM as the default implementation of fmap:

liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = do
    x <- m
    return (f x)

liftMfmap相同,除了专门用于monads之外,monads不像函子那么通用.

liftM is identical to fmap, except specialized to monads, which are not as general as functors.

因此,如果要转换IO动作的结果,则可以使用:

So if you want to transform the result of an IO action, you can either use:

  • fmap
  • liftM
  • do表示法
  • fmap,
  • liftM, or
  • do notation

您的偏好取决于您自己.我个人推荐fmap.

It's really up to you which one you prefer. I personally recommend fmap.