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 a
s to b
s, 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)
liftM
与fmap
相同,除了专门用于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
.