Ответ 1
Интерфейс 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
При сращивании на верхнем уровне с использованием шаблона 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
и некоторые другие полезные утилиты)