10

编码包在其构建脚本中使用HaXml (在 中Setup.hs。它碰巧使用了在 HaXml-1.19 和 HaXml-1.22 之间更改的接口位。如果编码包能够使用任一版本构建,那就太好了。我尝试使用通常的阴谋集团技巧,即做类似的事情

{-# LANGUAGE CPP #-}
#if MIN_VERSION_HaXml(1,22,0)
-- HaXml-1.22 code
#else
-- HaXml-1.19 code
#endif

...但是在配置包之前不能存在魔术定义,并且正在构建此文件以使配置步骤成为可能。我有哪些选择?有没有办法改变 cabal-install 调用 compile 的命令Setup.hs?是否有另一种有条件地选择避开阴谋集团的代码的机制?

4

2 回答 2

4

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

现在您可以创建和解构Elements,例如使用 ViewPatterns 扩展:

myElt :: String -> Element i
myElt = Elem (toQName "elemName") [] []

eltName :: Element i -> String
eltName (Elem (fromQName -> Just n) _ _) = n

ViewPatterns 很方便,但不是必需的,当然:对结果使用普通模式匹配fromQName也可以。

(这些想法促使我开发了notcpp 包,其中包括maybeReify和其他一些有用的实用程序)

于 2012-04-25T10:57:55.210 回答
2

似乎没有太多控制cabal-install/Distribution/Client/SetupWrapper.hs.是。Setup.hsSetup.hs

另一个技巧是制作安装脚本使用的兼容性 shim 库,该库具有适当的版本技巧。

但也许真正要问的问题是:为什么要Setup.hs使用外部库?

于 2012-04-23T16:32:01.853 回答