これまで見てきたすべての例では、Haskell XML ツールキット HXT を使用runX
してパーサーを実行していました。runX
IO モナド内で実行されます。この XML パーサーを IO 以外で使用する方法はありますか? 私には純粋な操作のように思えますが、なぜ私が IO の中にいることを余儀なくされているのか理解できません。
2 に答える
xread
と一緒にHXT を使用して、 のrunLA
外部で XML 文字列を解析できますIO
。
xread
次のタイプがあります。
xread :: ArrowXml a => a String XmlTree
これは、任意のタイプの矢印で構成して(ArrowXml a) => a XmlTree Whatever
を取得できることを意味しますa String Whatever
。
runLA
のようなrunX
ものですが、タイプのものの場合LA
:
runLA :: LA a b -> a -> [b]
LA
のインスタンスですArrowXml
。
これをすべてまとめるために、前の質問に対する私の回答の次のバージョンでは、 HXT を使用して、適切な形式の XML を含む文字列を何もIO
関与せずに解析します。
{-# LANGUAGE Arrows #-}
module Main where
import qualified Data.Map as M
import Text.XML.HXT.Arrow
classes :: (ArrowXml a) => a XmlTree (M.Map String String)
classes = listA (divs >>> pairs) >>> arr M.fromList
where
divs = getChildren >>> hasName "div"
pairs = proc div -> do
cls <- getAttrValue "class" -< div
val <- deep getText -< div
returnA -< (cls, val)
getValues :: (ArrowXml a) => [String] -> a XmlTree (String, Maybe String)
getValues cs = classes >>> arr (zip cs . lookupValues cs) >>> unlistA
where lookupValues cs m = map (flip M.lookup m) cs
xml = "<div><div class='c1'>a</div><div class='c2'>b</div>\
\<div class='c3'>123</div><div class='c4'>234</div></div>"
values :: [(String, Maybe String)]
values = runLA (xread >>> getValues ["c1", "c2", "c3", "c4"]) xml
main = print values
classes
以前のバージョンとgetValues
似ていますが、予想される入力と出力に合わせていくつかのマイナーな変更が加えられています。主な違いは、ここでは and の代わりに and を使用してxread
いるrunLA
ことreadString
ですrunX
。
同様に怠け者のようなものを読み取れるといいのですがByteString
、私の知る限り、これは現在 HXT では不可能です。
他のいくつかのこと:なしでこの方法で文字列を解析できIO
ますが、可能な場合はいつでも使用する方がおそらく良いでしょうrunX
: パーサーの構成、エラー メッセージなどをより詳細に制御できます。
また、例のコードを簡単で拡張しやすいものにしようとしましたが、必要に応じて、 と のコンビネータをControl.Arrow
使用Control.Arrow.ArrowList
すると、矢印をより簡潔に操作できます。以下は、classes
たとえばの同等の定義です。
classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
where pairs = getAttrValue "class" &&& deep getText
Travis Brown の回答は非常に役に立ちました。ここに独自のソリューションを追加したいだけです。これはもう少し一般的だと思います(同じ関数を使用し、問題固有の問題を無視するだけです)。
私は以前に解凍していました:
upIO :: XmlPickler a => String -> IO [a]
upIO str = runX $ readString [] str >>> arrL (maybeToList . unpickleDoc xpickle)
私はこれに変更することができました:
upPure :: XmlPickler a => String -> [a]
upPure str = runLA (xreadDoc >>> arrL (maybeToList . unpickleDoc xpickle)) str
これを行うと、パーサーなどの構成を制御しにくくなるという彼の意見に完全に同意します。これは残念なことです。