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

前端 未结 4 1837
清歌不尽
清歌不尽 2021-01-19 00:14

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 funct

相关标签:
4条回答
  • 2021-01-19 00:23

    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)
    }
    
    0 讨论(0)
  • 2021-01-19 00:25

    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 
    
    0 讨论(0)
  • 2021-01-19 00:37

    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.

    0 讨论(0)
  • 2021-01-19 00:46

    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.

    0 讨论(0)
提交回复
热议问题