有什么方法可以控制GHC如何派生`Eq`实例吗?

时间:2018-10-30 20:16:43

标签: haskell ghc

假设我有一个简单的枚举类型,包含10个元素:

data Test = A | B  | C | D | E | F | G | H | I | J
  deriving Eq

GHC如您所愿地导出了Eq实例:

==================== Derived instances ====================
Derived class instances:
  instance GHC.Classes.Eq Test.Test where
    (GHC.Classes.==) (Test.A) (Test.A) = GHC.Types.True
    (GHC.Classes.==) (Test.B) (Test.B) = GHC.Types.True
    (GHC.Classes.==) (Test.C) (Test.C) = GHC.Types.True
    (GHC.Classes.==) (Test.D) (Test.D) = GHC.Types.True
    (GHC.Classes.==) (Test.E) (Test.E) = GHC.Types.True
    (GHC.Classes.==) (Test.F) (Test.F) = GHC.Types.True
    (GHC.Classes.==) (Test.G) (Test.G) = GHC.Types.True
    (GHC.Classes.==) (Test.H) (Test.H) = GHC.Types.True
    (GHC.Classes.==) (Test.I) (Test.I) = GHC.Types.True
    (GHC.Classes.==) (Test.J) (Test.J) = GHC.Types.True
    (GHC.Classes.==) _ _ = GHC.Types.False

但是,一旦添加了第11个构造函数K,GHC就会更改实例的派生方式(表面上是出于效率原因):

==================== Derived instances ====================
Derived class instances:
  instance GHC.Classes.Eq Test.Test where
    (GHC.Classes.==) a_a1uD b_a1uE
      = case (Test.$con2tag_7MvV0ZqFtYZ8X4oGLC1npJ a_a1uD) of {
          a#_a1uF
            -> case (Test.$con2tag_7MvV0ZqFtYZ8X4oGLC1npJ b_a1uE) of {
                 b#_a1uG -> (GHC.Prim.tagToEnum# (a#_a1uF GHC.Prim.==# b#_a1uG)) } }

  Test.$con2tag_7MvV0ZqFtYZ8X4oGLC1npJ :: Test.Test -> GHC.Prim.Int#
  Test.$con2tag_7MvV0ZqFtYZ8X4oGLC1npJ a_a1uC
    = GHC.Base.getTag a_a1uC

问题

我正在使用一个使用Haskell源代码的工具,它可以轻松处理前一个漂亮的实例,但不能轻松处理后一个丑陋的实例。我希望有一个标志可以强制GHC始终生成漂亮的实例(即使实际运行的代码是在后台使用丑陋的实例秘密运行的)。理想情况下,我不必修改源代码(否则,我可能只是硬着头皮亲自编写漂亮的实例)。

这当前可行吗?

1 个答案:

答案 0 :(得分:5)

如果您对此足够关注,则可以为自己编写一些TH,以生成“更漂亮”的Eq实例。您可以从Ryan的deriving-compat软件包中获得启发,该软件包具有deriveEq :: Name -> Q [Dec](您不能直接使用它,因为Ryan始终如一地确保为数据类型使用完全相同的特殊逻辑,以包含10个构造函数。)

  

我希望有一个标志强制GHC始终生成漂亮的实例(即使实际运行的代码是在后台使用丑陋的实例秘密地进行的。)

不。看起来10是一个常数,硬编码为Eq的代码是如何生成的(source)。