有没有办法写下面的内容:
{-# 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解决方案的草图将会很好看.
这是一个简单易行的方法,而且很好但很难.正如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编译器中制作票证,让作者实现称为派生别名的功能:)
归档时间: |
|
查看次数: |
333 次 |
最近记录: |