2017-07-15 78 views
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),但这并不适用于deriving机制。随着约束种扩展,我发现,你可以这样写:

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

不幸的是,我不能忍受的是,deriving子句。有没有什么神奇的做法呢?

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

+3

'derivation'从句实际上是魔术。没有TH,恐怕没有办法做到你想做的事。 –

+2

考虑在https://github.com/haskell/rfcs中制作RFC – baxbaxwalanuksiwe

回答

5

还有不好和容易这样做的方法和不错,但很难的方式。由于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 

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

推出的解决方案草图之前,我将说明为什么这是很难与TH做。 deriving - 条款不是一些独立的条款,它是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 ] 
] 

现在你看到这种类型的类deriving被指定为DataD最后一个参数 - 数据声明 - 构造。解决您的问题的方法是使用-XStadandaloneDeriving extension。这就像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)) ] 

您可以直接使用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) 
     |] 

简短的教程can be found here

然后你就可以在其他文件中使用它(这是所谓的上演限制 TH限制:你应该定义在一个文件中宏观,但你不能在同一个文件中使用它)是这样的:

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

import Deriving 

data X = A | B | C 

boilerplateAnnigilator ''X 

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

deriving instance Eq a => MyData a 

如果你想为参数化类的TH微距为好,那么你基本上应该能够通过推断类型是否具有类型实现GHC编译器的整个逻辑变量或不变,并根据它们生成实例。但是这很难。我认为最好的解决方案就是在GHC编译器中制作票据,让作者实现衍生别名 :)

+0

您可以勾勒TH解决方案吗? –

+0

@LeventErkok我写了TH解决方案的小图。现在你可以看到为什么它不那么微不足道了:) – Shersh

+0

感谢您的最有帮助的写作。这工作就像我的用例魅力!非常感激。 –