简体   繁体   English

Haskell:陷入 IO monad

[英]Haskell: Trapped in IO monad

I am trying to parse a file using the parseFile function found in the the haskell-src-exts package.我正在尝试使用haskell-src-exts包中的parseFile函数解析文件。

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 .我正在尝试使用parseFile的输出,这当然是IO ,但我不知道如何绕过IO I found a function liftIO but I am not sure if that is the solution in this situation.我找到了一个函数liftIO但我不确定这是否是这种情况下的解决方案。 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 .我只想能够在parseFile的输出上使用pMod'函数。

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

Once inside IO, there's no escape.一旦进入 IO,就无处可逃。

Use fmap :使用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 另外:)正如Levi Pearson很好的回答中所解释的那样,还有

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顺便说一句, >>= return . f >>= return . f bit is how liftM is actually implemented, only in do -notation; >>= return . f位是liftM的实际实现方式,仅在do表示法中; and it really shows the equivalency of fmap and liftM , because for any monad it should hold that:并且它确实显示了fmapliftM的等价性,因为对于任何 monad 都应该保持:

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

To give a more general answer than Will's (which was certainly correct and to-the-point), you typically 'lift' operations into a Monad rather than taking values out of them in order to apply pure functions to monadic values.为了让比威尔的更普遍的答案(这当然正确,到了点),你通常“电梯”业务并入一个单子,而不是为了申请纯函数单子值取值了出来

It so happens that Monad s (theoretically) are a specific kind of Functor .碰巧Monad s(理论上)是一种特定类型的Functor Functor describes the class of types that represent mappings of objects and operations into a different context. Functor描述了表示对象和操作到不同上下文的映射的类型类。 A data type that is an instance of Functor maps objects into its context via its data constructors and it maps operations into its context via the fmap function.作为Functor实例的数据类型通过其数据构造函数将对象映射到其上下文中,并通过fmap函数将操作映射到其上下文中。 To implement a true functor, fmap must work in such a way that lifting the identity function into the functor context does not change the values in the functor context, and lifting two functions composed together produces the same operation within the functor context as lifting the functions separately and then composing them within the functor context.为了实现真正的函子, fmap必须以这样一种方式工作, fmap恒等函数提升到函子上下文中不会改变函子上下文中的值,并且提升两个组合在一起的函数在函子上下文中产生与提升函数相同的操作分开,然后在函子上下文中组合它们。

Many, many Haskell data types naturally form functors, and fmap provides a universal interface to lift functions so that they apply 'evenly' throughout the functorized data without worrying about the form of the particular Functor instance.许多、许多 Haskell 数据类型自然形成函子,而fmap提供了一个通用接口来提升函数,以便它们“均匀地”应用于整个Functor化数据,而无需担心特定Functor实例的形式。 A couple of great examples of this are the list type and the Maybe type;一些很好的例子是列表类型和Maybe类型; fmap of a function into a list context is exactly the same as the familiar map operation on lists, and fmap of a function into a Maybe context will apply the function normally for a Just a value and do nothing for a Nothing value, allowing you to perform operations on it without worrying about which it is.将函数fmap到列表上下文中与熟悉的列表上的map操作fmap ,将函数fmapMaybe上下文中将正常应用函数对Just a值而对Nothing值不做任何Nothing ,允许您对其执行操作而不必担心它是哪个。

Having said all that, by a quirk of history the Haskell Prelude doesn't currently require Monad instances to also have a Functor instance, so Monad provides a family of functions that also lift operations into monadic contexts.话虽如此,根据历史的怪癖,Haskell Prelude 目前不需要Monad实例也有一个Functor实例,因此Monad提供了一系列函数,这些函数也将操作提升到 monadic 上下文中。 The operation liftM does the same thing that fmap does for Monad instances that are also Functor instances (as they should be).操作liftM做同样的事情fmap确实为Monad实例,同时也是Functor实例(因为他们应该)。 But fmap and liftM only lift single-argument functions.但是fmapliftM只提升单参数函数。 Monad helpfully provides a family of liftM2 -- liftM5 functions that lift multi-argument functions into monadic context in the same way. Monad提供了一系列liftM2 —— liftM5函数,它们以相同的方式将多参数函数提升到monadic 上下文中。

Finally, you asked about liftIO , which brings in the related idea of monad transformers , in which multiple Monad instances are combined in a single data type by applying monad mappings to already-monadic values, forming a kind of stack of monadic mappings over a basic pure type.最后,你问到了liftIO ,它带来了 monad转换器的相关思想,其中通过将 monad 映射应用到已经存在的 monadic 值,将多个Monad实例组合成一个单一的数据类型,在一个基本的基础上形成一种 monadic 映射堆栈。纯类型。 The mtl library provides one implementation of this general idea, and in its module Control.Monad.Trans it defines two classes, MonadTrans t and Monad m => MonadIO m . mtl库提供了这一总体思想的一种实现,并且在其模块Control.Monad.Trans定义了两个类, MonadTrans tMonad m => MonadIO m The MonadTrans class provides a single function, lift , that gives access to operations in the next higher monadic "layer" in the stack, ie (MonadTrans t, Monad m) => ma -> tma . MonadTrans类提供了一个函数lift ,它可以访问堆栈中下一个更高的 monadic “层”中的操作,即(MonadTrans t, Monad m) => ma -> tma The MonadIO class provides a single function, liftIO , that provides access to IO monad operations from any "layer" in the stack, ie IO a -> ma . MonadIO类提供了一个单一的函数, liftIO ,它提供了对来自堆栈中任何“层”的IO monad 操作的访问,即IO a -> ma These make working with monad transformer stacks much more convenient at the cost of having to provide a lot of transformer instance declarations when new Monad instances are introduced to a stack.这些使得使用 monad 转换器堆栈更加方便,代价是在将新的Monad实例引入堆栈时必须提供大量转换器实例声明。

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

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