[英]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:并且它确实显示了
fmap
和liftM
的等价性,因为对于任何 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
,将函数fmap
到Maybe
上下文中将正常应用函数对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.但是
fmap
和liftM
只提升单参数函数。 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 t
和Monad 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.