Интерфейс 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
Когда сращены на верхнем уровне с использованием шаблона 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 package, который включает в себя maybeReify
и некоторые другие полезные утилиты)
Насколько серьезны изменения в интерфейсе? Каковы изменения? Возможно, шаблон haskell может спасти вас, в зависимости от того, что они собой представляют. –
@benmachine Единственное реальное изменение заключается в том, что конструктор 'Element' теперь принимает значение типа' data QName = N String | SomethingIDon'tCareAbout' вместо 'String'. Файл 'Setup.hs' использует конструктор' Element' как функцию (всегда с литералом 'String') и для сопоставления шаблонов (иногда с литералом' String' и иногда с шаблоном переменной catch-all). –
Итак, было бы достаточно, если бы у вас были функции 'toQName' и' fromQName', так что, когда 'QName' существует,' toQName = N' и 'fromQName' превращает' N s' в 'Just s' (и все остальное в' Nothing '), тогда как' QName' не существует, 'toQName = id' и' fromQName = Just'? Тогда вы могли бы сделать то, что хотите, с шаблонами просмотра? Я думаю, что 'toQName' и' fromQName' могут быть определены с помощью шаблона haskell, используя идеи, аналогичные идеям в моем [пакете notcpp] (http://hackage.haskell.org/package/notcpp) –