2013-08-13 2 views
7

私はhaskell-src-extsパッケージにあるparseFile関数を使ってファイルを解析しようとしています。私はもちろんIOであるparseFileの出力を処理しようとしていますが、IOを回避する方法を理解することはできません。私はfunction liftIOを見つけましたが、それがこの状況の解決策であるかどうかはわかりません。以下にコードを示します。Haskell:IO Monadにトラップされました

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にあることに注意してください。前もって感謝します!

答えて

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 

加え :)としてはgreat answer by 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ビットはliftMが実際にどのように実装されているか、do -notationでのみです。すべてのモナドのためにそれはそれを保持しなければならないので、それは本当に、fmapliftMの等価性を示しています

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

ありがとう、これはとてもうまくいくようです! – user2548080

14

ウィルの(確かに正しいとTO-点だった)よりも一般的な答えを与えるために、一般的にあなたモナド値に純粋な関数を適用するために、に変換して、に置き換えずに返します。

Monad(理論上)は特定の種類のFunctorです。 Functorは、異なるコンテキストへのオブジェクトおよび操作のマッピングを表すタイプのクラスを記述する。 Functorのインスタンスであるデータ型は、データコンストラクタを介してオブジェクトをそのコンテキストにマッピングし、fmap関数を介して操作をそのコンテキストにマップします。真のファンクタを実装するには、fmapは、ファンクタコンテキストにIDファンクションを持たせてファンクタコンテキストの値を変更しないで、ファンクタコンテキスト内でファンクタコンテキスト内で同じオペレーションを生成するファンクタのコンテキスト内でそれらを構成することができます。

多く、多くのHaskellのデータ・タイプは、天然にファンクタを形成し、fmapは、それらが特定のFunctorインスタンスの形を気にせずに「均等」functorizedデータ全体に適用するように機能を持ち上げるために汎用インターフェースを提供します。これの素晴らしい例の2つはリストタイプとMaybeタイプです。 fmapの関数のリストコンテキストへのアクセスは、リスト上でよく知られているmapの操作とまったく同じであり、Maybeコンテキスト内の関数のfmapは、Just a値に対して通常の機能を適用し、Nothing値に対しては何も実行せず、それがどれであるか心配することなくそれを操作することができます。

すべてのことを言って、歴史の気まぐれでHaskellのプレリュードは、現在もFunctorインスタンスを持つことMonadのインスタンスを必要としないので、Monadもモナドコンテキストに操作を持ち上げる機能ファミリを提供しています。操作liftMは、がMonadインスタンスの場合も同様です(同じように)Functorインスタンスです。しかし、fmapliftMは、単一引数の関数のみを持ち上げます。 Monadは、同じように多引数関数をモナドコンテキストに持ち上げる関数liftM2 - liftM5を提供します。

最後に、あなたはのようなものを形成し、およそliftIOMonadインスタンスはモナドへのマッピング既にモナドの値を適用することによって、単一のデータ型に組み合わせた複数ので変圧モナドの関連アイデアにもたらしている尋ねました基本的な純粋な型よりもモナドマッピングのスタックmtlライブラリは、この一般的なアイデアの1つの実装を提供し、モジュールControl.Monad.Transでは、2つのクラスMonadTrans tMonad m => MonadIO mを定義します。 MonadTransクラスは、という1つの関数を提供します。この関数は、スタック内の次に高いモナドの「レイヤー」の操作、つまり(MonadTrans t, Monad m) => m a -> t m aにアクセスします。 MonadIOクラスは、liftIOという単一の関数を提供し、スタック内の任意の「レイヤー」からのIOモナド操作へのアクセスを提供します。つまり、IO a -> m aです。これらは、新しいMonadインスタンスがスタックに導入されたときに多くのトランスインスタンスの宣言を提供しなければならず、モナド・トランス・スタックで作業するほうがはるかに便利です。

+0

詳しい説明をありがとうございます!私はモナドとファンクターをもう少し理解しているように感じます(以前は全く理解していませんでした)。 – user2548080

+3

私がここで質問に答える理由の1つは、プログラミング中に自然に来るように、これらの概念を強化することです。異なるタイプのクラスがどのように協調して動作するかを考えた後、実際に不透明なHaskellコードは突然透過的になります。がんばろう! –

関連する問題