インターフェイスは、Data.Data
存在する場合と存在しない場合があるタイプの値を構築および分解することができます(ほぼ!)。残念ながら、HaXmlにはData
その型のインスタンスがないようです。また、存在する場合と存在しない場合がある型を参照できないため、インスタンスを定義できません。そのため、テンプレートHaskellを使用する必要があります。
次のモジュールはエクスポートしますqnameCompat
:
{-# LANGUAGE TemplateHaskell #-}
module HaXmlCompat (qnameCompat) where
import Language.Haskell.TH
qnameCompat :: Q [Dec]
qnameCompat = do
mi <- maybeReify "N"
case mi of
Nothing -> sequence [
tySynD (mkName "QName") [] [t| String |],
valD [p| toQName |] (normalB [| id |]) [],
valD [p| fromQName |] (normalB [| Just |]) []]
Just (DataConI n _ _ _) -> do
s <- newName "s"
sequence [
valD [p| toQName |] (normalB (conE n)) [],
funD (mkName "fromQName") [
clause [conP n [varP s]] (normalB (appE [| Just |] (varE s))) [],
clause [ [p| _ |] ] (normalB [| Nothing |]) []]]
Just i -> fail $
"N exists, but isn't the sort of thing I expected: " ++ show i
maybeReify :: String -> Q (Maybe Info)
maybeReify = recover (return Nothing) . fmap Just . reify . mkName
Template Haskellを使用してトップレベルでスプライスされると、存在qnameCompat
するかどうかをチェックN
します。含まれている場合は、次のコードが生成されます。
toQName = N
fromQName (N s) = Just s
fromQName _ = Nothing
そうでない場合は、次のように生成されます。
type QName = String
toQName = id
fromQName = Just
Element
これで、たとえばViewPatterns拡張機能を使用して、を作成および分解できます。
myElt :: String -> Element i
myElt = Elem (toQName "elemName") [] []
eltName :: Element i -> String
eltName (Elem (fromQName -> Just n) _ _) = n
ViewPatternsは便利ですが、もちろん必須ではありません。結果に通常のパターンマッチングを使用しfromQName
ても、同様に機能します。
(これらのアイデアは、私がnotcppパッケージmaybeReify
を開発するきっかけとなりました。これには、その他の便利なユーティリティが含まれています)