代码之家  ›  专栏  ›  技术社区  ›  alias

有没有办法缩短这个派生子句?

  •  8
  • alias  · 技术社区  · 7 年前

    有没有办法写出以下内容:

     {-# LANGUAGE DeriveDataTypeable #-}
     {-# LANGUAGE DeriveAnyClass     #-}
    
     data X = A | B | C
         deriving (Eq, Ord, Show, Read, Data, SymWord, HasKind, SMTValue)
    

    deriving 子句可以以某种方式缩短为以下内容:

     data X = A | B | C deriving MyOwnClass
    

    如果可能的话,我想避免TH,我很乐意创建一个新类,根据需要将所有这些派生类作为其超类(如中所示) MyOwnClass 推导 机械装置通过约束种类扩展,我发现您可以编写以下内容:

    type MyOwnClass a = (Eq a, Ord a, Show a, Read a, Data a, SymWord a, HasKind a, SMTValue a)
    

    不幸的是,我不能把它放在 推导

    编辑 从评论来看,这可能是这里唯一可行的选择。(CPP宏确实不正常!)如果是这样的话,第TH个解决方案的草图会很好看。

    1 回复  |  直到 7 年前
        1
  •  8
  •   Shersh    7 年前

    糟糕而简单 好但难 方法像 Silvio Mayolo TemplateHaskell 编写这样的函数。这是一种艰难而复杂的方式。更简单的方法是像这样使用C预处理器:

    {-# LANGUAGE CPP #-}
    
    #define MY_OWN_CLASS (Eq, Ord, Show, Read, Data, SymWord, HasKind, SMTValue)
    
    data X = A | B | C
         deriving MY_OWN_CLASS
    

    想法和;TH解决方案示意图

    在介绍解决方案的草图之前,我将说明为什么使用TH很难做到这一点。 deriving -子句不是独立的子句,它是 data 推导 runQ 命令,看看最后应该写些什么。这样地:

    ghci> :set -XTemplateHaskell
    ghci> :set -XQuasiQuotes 
    ghci> import Language.Haskell.TH
    ghci> runQ [d|data A = B deriving (Eq, Show)|]
    [ DataD
        []
        A_0
        []
        Nothing
        [ NormalC B_1 [] ]
        [ ConT GHC.Classes.Eq , ConT GHC.Show.Show ]
    ]
    

    现在你看到了 DataD 数据声明构造函数。解决问题的方法是使用 -XStadandaloneDeriving extension 但是功能强大,但也很冗长。再说一次,要想知道你到底想生成什么,只需使用 runQ公司 :

    ghci> data D = T
    ghci> :set -XStandaloneDeriving 
    ghci> runQ [d| deriving instance Show D |]
    [ StandaloneDerivD [] (AppT (ConT GHC.Show.Show) (ConT Ghci5.D)) ]
    

    您可以使用 StandaloneDerivD 和其他构造函数直接或仅使用 [d|...|] -括号虽然有更多的魔力,但它们给你列出了 Dec (声明)。如果要生成多个声明,则应按以下方式编写函数:

    {-# LANGUAGE TemplateHaskell    #-}
    {-# LANGUAGE QuasiQuotes        #-}
    {-# LANGUAGE StandaloneDeriving #-}
    
    module Deriving where
    
    import Language.Haskell.TH
    
    boilerplateAnnigilator :: Name -> Q [Dec]
    boilerplateAnnigilator typeName = do
        let typeCon = conT typeName
        [d|deriving instance Show $(typeCon)
           deriving instance Eq   $(typeCon)
           deriving instance Ord  $(typeCon)
          |]
    

    简要教程 can be found here

    然后你可以在另一个文件中使用它(这是一个名为 阶段性限制 :您应该定义

    {-# LANGUAGE StandaloneDeriving #-}
    {-# LANGUAGE TemplateHaskell    #-}
    
    import Deriving
    
    data X = A | B | C
    
    boilerplateAnnigilator ''X
    

    你应该把你想要的其他类型类放进去 boilerplateAnnigilator 作用但这种方法只适用于非参数化类。如果你有 data MyData a = ... 独立衍生 应该看起来像:

    deriving instance Eq a => MyData a
    

    导出别名 :)