Haskell - How can I use pure functions inside IO f

2020-04-07 07:01发布

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

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.

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

4条回答
乱世女痞
2楼-- · 2020-04-07 07:14

Alex Horsman helped me. He said:

"Perhaps I'm misunderstanding, but that sounds pretty simple? do {x <- ioFunc; return (pureFunc x)}"

And then I solved my problem:

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)
}
查看更多
神经病院院长
3楼-- · 2020-04-07 07:23

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

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

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

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

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

map toUpper :: String -> String

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

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

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

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

We can also implement this using do notation, to:

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

>>> getUpperCase
Test<Enter>
TEST

It turns out that every monad has the following property:

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

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 is identical to fmap, except specialized to monads, which are not as general as functors.

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

  • fmap,
  • liftM, or
  • do notation

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

查看更多
在下西门庆
4楼-- · 2020-04-07 07:28

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 
查看更多
Melony?
5楼-- · 2020-04-07 07:38

The actual answer is as follows:

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

return wraps it in the appropriate monad so that do can assign it to val.

查看更多
登录 后发表回答