haskelliomonadspure-function

Haskell: Trapped in IO monad


I am trying to parse a file using the parseFile function found in the the haskell-src-exts package.

I am trying to work with the output of parseFile which is of course IO, but I can't figure out how to get around the IO. I found a function liftIO but I am not sure if that is the solution in this situation. Here is the code below.

import Language.Haskell.Exts.Syntax
import Language.Haskell.Exts 
import Data.Map hiding (foldr, map)
import Control.Monad.Trans

increment :: Ord a => a -> Map a Int -> Map a Int
increment a = insertWith (+) a 1

fromName :: Name -> String
fromName (Ident s) = s
fromName (Symbol st) = st

fromQName :: QName -> String
fromQName (Qual _ fn) = fromName fn
fromQName (UnQual n) = fromName n

fromLiteral :: Literal -> String
fromLiteral (Int int) = show int

fromQOp :: QOp -> String
fromQOp (QVarOp qn) = fromQName qn

vars :: Exp -> Map String Int
vars (List (x:xs)) = vars x
vars (Lambda _ _ e1) = vars e1
vars (EnumFrom e1) = vars e1
vars (App e1 e2) = unionWith (+) (vars e1) (vars e2)
vars (Let _ e1) = vars e1
vars (NegApp e1) = vars e1
vars (Var qn) = increment (fromQName qn) empty
vars (Lit l) = increment (fromLiteral l) empty
vars (Paren e1) = vars e1
vars (InfixApp exp1 qop exp2) = 
                 increment (fromQOp qop) $ 
                     unionWith (+) (vars exp1) (vars exp2)



match :: [Match] -> Map String Int
match rhss = foldr (unionWith (+) ) empty 
                    (map (\(Match  a b c d e f) -> rHs e) rhss)

rHS :: GuardedRhs -> Map String Int
rHS (GuardedRhs _ _ e1) = vars e1

rHs':: [GuardedRhs] -> Map String Int
rHs' gr = foldr (unionWith (+)) empty 
                 (map (\(GuardedRhs a b c) -> vars c) gr)

rHs :: Rhs -> Map String Int
rHs (GuardedRhss gr) = rHs' gr
rHs (UnGuardedRhs e1) = vars e1

decl :: [Decl] -> Map String Int
decl decls =  foldr (unionWith (+) ) empty 
                     (map fun decls )
    where fun (FunBind f) = match f
          fun _ = empty

pMod' :: (ParseResult Module) -> Map String Int
pMod' (ParseOk (Module _ _ _ _ _ _ dEcl)) = decl dEcl 

pMod :: FilePath -> Map String Int
pMod = pMod' . liftIO . parseFile 

I just want to be able to use the pMod' function on the output of parseFile.

Note that all the types and data constructors can be found at http://hackage.haskell.org/packages/archive/haskell-src-exts/1.13.5/doc/html/Language-Haskell-Exts-Syntax.html if that helps. Thanks in advance!


Solution

  • Once inside IO, there's no escape.

    Use fmap:

    -- parseFile :: FilePath -> IO (ParseResult Module)
    -- pMod' :: (ParseResult Module) -> Map String Int
    -- fmap :: Functor f => (a -> b) -> f a -> f b
    
    -- fmap pMod' (parseFile filePath) :: IO (Map String Int)
    
    pMod :: FilePath -> IO (Map String Int)
    pMod = fmap pMod' . parseFile 
    

    (addition:) As explained in great answer by Levi Pearson, there's also

    Prelude Control.Monad> :t liftM
    liftM :: (Monad m) => (a1 -> r) -> m a1 -> m r
    

    But that's no black magic either. Consider:

    Prelude Control.Monad> let g f = (>>= return . f)
    Prelude Control.Monad> :t g
    g :: (Monad m) => (a -> b) -> m a -> m b
    

    So your function can also be written as

    pMod fpath = fmap pMod' . parseFile $ fpath
         = liftM pMod' . parseFile $ fpath
         = (>>= return . pMod') . parseFile $ fpath   -- pushing it...
         = parseFile fpath >>= return . pMod'         -- that's better
    
    pMod :: FilePath -> IO (Map String Int)
    pMod fpath = do
        resMod <- parseFile fpath
        return $ pMod' resMod
    

    whatever you find more intuitive (remember, (.) has the highest precedence, just below the function application).

    Incidentally, the >>= return . f bit is how liftM is actually implemented, only in do-notation; and it really shows the equivalency of fmap and liftM, because for any monad it should hold that:

    fmap f m  ==  m >>= (return . f)