该Data.Data
接口能够(几乎!)构造和解构可能存在或不存在的类型的值。不幸的是,HaXml 似乎没有Data
其类型的实例,并且您无法定义实例,因为您无法引用可能存在或可能不存在的类型,因此我们不得不求助于 Template 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
s,例如使用 ViewPatterns 扩展:
myElt :: String -> Element i
myElt = Elem (toQName "elemName") [] []
eltName :: Element i -> String
eltName (Elem (fromQName -> Just n) _ _) = n
ViewPatterns 很方便,但不是必需的,当然:对结果使用普通模式匹配fromQName
也可以。
(这些想法促使我开发了notcpp 包,其中包括maybeReify
和其他一些有用的实用程序)