有
糟糕而简单
好但难
方法像
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
导出别名
:)