有没有办法缩短这个推导条款?

ali*_*ias 8 haskell deriving

有没有办法写下面的内容:

 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE DeriveAnyClass     #-}

 data X = A | B | C
     deriving (Eq, Ord, Show, Read, Data, SymWord, HasKind, SMTValue)
Run Code Online (Sandbox Code Playgroud)

因此deriving可以以某种方式缩短该子句,如下所示:

 data X = A | B | C deriving MyOwnClass
Run Code Online (Sandbox Code Playgroud)

我想尽可能避免TH,并且我很乐意创建一个新类,它将所有派生类作为必要的超类(MyOwnClass如上所述),但这并不适用于deriving机制.通过约束种类扩展,我发现你可以这样写:

type MyOwnClass a = (Eq a, Ord a, Show a, Read a, Data a, SymWord a, HasKind a, SMTValue a)
Run Code Online (Sandbox Code Playgroud)

不幸的是,我不能把它放在deriving条款中.是否有一些魔力可以实现这一目标?

编辑从评论中看来,TH可能是唯一可行的选择.(CPP宏真的不行!)如果是这样的话,TH解决方案的草图将会很好看.

She*_*rsh 8

这是一个简单易行的方法,而且很好但很难.正如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
Run Code Online (Sandbox Code Playgroud)

更新(2016年7月17日): TH解决方案的想法和草图

在介绍解决方案草图之前,我将说明为什么TH更难做到这一点.deriving-clause不是一些独立的子句,它是data声明的一部分,所以你不能不仅仅编码部分内部deriving.编写任何TH代码的一般方法是使用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 ]
]
Run Code Online (Sandbox Code Playgroud)

现在您看到类型类deriving被指定为DataD- 数据声明 - 构造函数的最后一个参数.您的问题的解决方法是使用-XStadandaloneDeriving扩展.deriving虽然也很冗长,但它很强大但功能强大.再次,看看,你想要生成什么,只需使用runQ:

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

你可以StandaloneDerivD直接使用和其他构造函数,或者只是使用[d|...|]-brackets虽然它们有更多魔法,但它们会给你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)
      |]
Run Code Online (Sandbox Code Playgroud)

可在此处找到简要教程.

然后你可以在另一个文件中使用它(这是TH限制,称为分阶段限制:你应该在一个文件中定义但你不能在同一个文件中使用它),如下所示:

{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell    #-}

import Deriving

data X = A | B | C

boilerplateAnnigilator ''X
Run Code Online (Sandbox Code Playgroud)

你应该在boilerplateAnnigilator函数中放入你想要的其他类型类.但这种方法仅适用于非参数化类.如果你data MyData a = ...独立位置推导应该是这样的:

deriving instance Eq a => MyData a
Run Code Online (Sandbox Code Playgroud)

如果你希望你的TH宏也适用于参数化类,那么你基本上应该通过推断类型是否具有类型变量来实现GHC编译器的整个逻辑,并根据它生成实例.但这要困难得多.我认为最好的解决方案是在GHC编译器中制作票证,让作者实现称为派生别名的功能:)