Haskell - How can I use pure functions inside IO functions?

孤街醉人 提交于 2019-12-20 06:44:09

问题


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?


回答1:


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.




回答2:


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:


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 



回答4:


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.



来源:https://stackoverflow.com/questions/58601901/how-to-use-an-io-function-with-conditional-compilation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!