我试图使用haskell-src-exts包中的parseFile函数解析文件.我正在尝试使用parseFile的输出,这当然是IO,但我无法弄清楚如何绕过IO.我找到了一个函数liftIO,但我不确定这是否是这种情况下的解决方案.这是下面的代码.
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
Run Code Online (Sandbox Code Playgroud)
我只是希望能够在parseFile的输出上使用pMod'函数.请注意,所有的数据的类型和构造可以在这里找到http://hackage.haskell.org/packages/archive/haskell-src-exts/1.13.5/doc/html/Language-Haskell-Exts-Syntax.html如果这有帮助.提前致谢!
Wil*_*ess 16
一旦进入IO,就没有逃脱.
用途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
Run Code Online (Sandbox Code Playgroud)
(另外 :)正如Levi Pearson的精彩回答所解释的那样,也有
Prelude Control.Monad> :t liftM
liftM :: (Monad m) => (a1 -> r) -> m a1 -> m r
Run Code Online (Sandbox Code Playgroud)
但这也不是黑魔法.考虑:
Prelude Control.Monad> let g f = (>>= return . f)
Prelude Control.Monad> :t g
g :: (Monad m) => (a -> b) -> m a -> m b
Run Code Online (Sandbox Code Playgroud)
所以你的功能也可以写成
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
Run Code Online (Sandbox Code Playgroud)
无论你发现什么更直观(记住,(.)具有最高优先级,就在功能应用程序下面).
顺便说一句,该>>= return . f位是如何liftM实际实现的,只是do注释; 并且它确实显示了fmap和的等价性liftM,因为对于任何monad它应该认为:
fmap f m = m >>= (return . f)
Run Code Online (Sandbox Code Playgroud)
Lev*_*son 15
为了让比威尔(这当然正确,到了点),你通常"电梯"操作的更普遍的答案变成一个单子,而不是取值了,为了他们的纯功能适用于单子值.
事实上,Monads(理论上)是一种特定的类型Functor.Functor描述了表示对象和操作到不同上下文的映射的类型类.一种数据类型,它是Functor通过其数据构造函数将对象映射到其上下文的实例,并通过该fmap函数将操作映射到其上下文中.要实现真正的仿函数,fmap必须以这样的方式工作:将标识函数提升到仿函数上下文中不会更改仿函数上下文中的值,并且提升组合在一起的两个函数会在仿函数上下文中生成相同的操作,因为单独提升函数然后在仿函数上下文中组合它们.
许多Haskell数据类型自然形成仿函数,并fmap提供了一个通用接口来提升函数,以便它们在整个仿函数数据中"均匀"应用,而不必担心特定Functor实例的形式.列表类型和Maybe类型的几个很好的例子; fmap将函数放入列表上下文与列表中熟悉的map操作完全相同fmap,将函数放入Maybe上下文中会将函数正常应用于Just a值Nothing而不对值执行任何操作,从而允许您对其执行操作而无需担心它是什么.
尽管如此,通过历史的怪癖,Haskell Prelude目前还不需要Monad实例也有一个Functor实例,因此Monad提供了一系列功能,这些功能也将操作提升到一元上下文中.该操作对于也是实例的实例(就像它们应该这样)也会liftM执行相同的操作.但与仅提升单参数的函数.有用地提供了一系列- 以相同方式将多参数函数提升到monadic上下文中的函数.fmapMonadFunctorfmapliftMMonadliftM2liftM5
最后,你问了一下liftIO,它引入了monad 变换器的相关概念,其中多个Monad实例通过将monad映射应用于已经monadic的值组合成单个数据类型,形成一种基本纯类型的monadic映射堆栈.该MTL库提供了一个实现这一总体思路,它的模块中Control.Monad.Trans它定义了两个班,MonadTrans t和Monad m => MonadIO m.的MonadTrans类提供了一个单一的功能,lift即给出在堆栈中的下一个较高的一元"层",即访问操作(MonadTrans t, Monad m) => m a -> t m a.的MonadIO类提供了一个单一的功能,liftIO即提供了访问IO从在堆栈中任何"层",即单子操作IO a -> m a.这些使得使用monad变换器堆栈更加方便,但是在将新Monad实例引入堆栈时必须提供大量变换器实例声明.