繁体   English   中英

Haskell:陷入 IO monad

[英]Haskell: Trapped in IO monad

我正在尝试使用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 

我只想能够在parseFile的输出上使用pMod'函数。

请注意,所有类型和数据构造函数都可以在http://hackage.haskell.org/packages/archive/haskell-src-exts/1.13.5/doc/html/Language-Haskell-Exts-Syntax.html找到,如果这有帮助。 提前致谢!

一旦进入 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 

另外:)正如Levi Pearson很好的回答中所解释的那样,还有

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

但这也不是黑魔法。 考虑:

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

所以你的函数也可以写成

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

无论觉得更直观(记住, (.)具有最高优先级,就在函数应用程序的下方)

顺便说一句, >>= return . f >>= return . f位是liftM的实际实现方式,仅在do表示法中; 并且它确实显示了fmapliftM的等价性,因为对于任何 monad 都应该保持:

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

为了让比威尔的更普遍的答案(这当然正确,到了点),你通常“电梯”业务并入一个单子,而不是为了申请纯函数单子值取值了出来

碰巧Monad s(理论上)是一种特定类型的Functor Functor描述了表示对象和操作到不同上下文的映射的类型类。 作为Functor实例的数据类型通过其数据构造函数将对象映射到其上下文中,并通过fmap函数将操作映射到其上下文中。 为了实现真正的函子, fmap必须以这样一种方式工作, fmap恒等函数提升到函子上下文中不会改变函子上下文中的值,并且提升两个组合在一起的函数在函子上下文中产生与提升函数相同的操作分开,然后在函子上下文中组合它们。

许多、许多 Haskell 数据类型自然形成函子,而fmap提供了一个通用接口来提升函数,以便它们“均匀地”应用于整个Functor化数据,而无需担心特定Functor实例的形式。 一些很好的例子是列表类型和Maybe类型; 将函数fmap到列表上下文中与熟悉的列表上的map操作fmap ,将函数fmapMaybe上下文中将正常应用函数对Just a值而对Nothing值不做任何Nothing ,允许您对其执行操作而不必担心它是哪个。

话虽如此,根据历史的怪癖,Haskell Prelude 目前不需要Monad实例也有一个Functor实例,因此Monad提供了一系列函数,这些函数也将操作提升到 monadic 上下文中。 操作liftM做同样的事情fmap确实为Monad实例,同时也是Functor实例(因为他们应该)。 但是fmapliftM只提升单参数函数。 Monad提供了一系列liftM2 —— liftM5函数,它们以相同的方式将多参数函数提升到monadic 上下文中。

最后,你问到了liftIO ,它带来了 monad转换器的相关思想,其中通过将 monad 映射应用到已经存在的 monadic 值,将多个Monad实例组合成一个单一的数据类型,在一个基本的基础上形成一种 monadic 映射堆栈。纯类型。 mtl库提供了这一总体思想的一种实现,并且在其模块Control.Monad.Trans定义了两个类, MonadTrans tMonad m => MonadIO m MonadTrans类提供了一个函数lift ,它可以访问堆栈中下一个更高的 monadic “层”中的操作,即(MonadTrans t, Monad m) => ma -> tma MonadIO类提供了一个单一的函数, liftIO ,它提供了对来自堆栈中任何“层”的IO monad 操作的访问,即IO a -> ma 这些使得使用 monad 转换器堆栈更加方便,代价是在将新的Monad实例引入堆栈时必须提供大量转换器实例声明。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM