acf*_*zer 11 haskell template-haskell
我正在从矢量空间为OpenGL类型定义类的实例,并且为了节省我的打字肌肉,我想使用Template Haskell为我编写一堆实例.
我开始时通过定义函数来派生实例AdditiveGroup:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Data.VectorSpace.OpenGL.TH where
import Control.Applicative
import Control.Monad
import Data.AdditiveGroup
import Data.VectorSpace
import Language.Haskell.TH
deriveScalarAdditive ts = concat <$> forM (map conT ts) (\t -> [d|
instance AdditiveGroup $t where zeroV = 0; (^+^) = (+); negateV = negate
|])
Run Code Online (Sandbox Code Playgroud)
这很好,但请注意,我只是拼接$t到牛津支架一次.现在,派生VectorSpace实例的函数:
deriveScalarVectorSpace ts = concat <$> forM (map conT ts) (\t -> [d|
instance VectorSpace $t where type Scalar $t = $t; (*^) = (*)
|])
Run Code Online (Sandbox Code Playgroud)
但是,这个barfs:
Type indexes must match class instance head
Found `t_tt' but expected `t_ts'
In the associated type instance for `Scalar'
In the instance declaration for `VectorSpace $t'
In the Template Haskell quotation
[d| instance VectorSpace $t where
type instance Scalar $t = $t
{ *^ = (*) } |]
Run Code Online (Sandbox Code Playgroud)
错误t_ts和t_tt错误之间的区别告诉我TH每次拼接$t时都会创建一个新的,唯一的名称,当然,只有这些类型相同时,定义才会起作用.
有没有办法通过牛津括号获得我想要的行为,还是我必须回到良好的旧词法范围和Language.Haskell.TH组合器?我知道CPP可能会更容易,但我想借此机会学习一些TH.
我想你必须使用Language.Haskell.TH组合器.请参阅以下门票:
这样做非常简单.我会从这开始(稍微格式化)
*Foo Language.Haskell.TH> runQ (deriveScalarAdditive [''Int] ) >>= print
[InstanceD [] (AppT (ConT Data.AdditiveGroup.AdditiveGroup) (ConT GHC.Types.Int))
[ValD (VarP zeroV_12) (NormalB (LitE (IntegerL 0))) [],
ValD (VarP ^+^_13) (NormalB (VarE GHC.Num.+)) [],
ValD (VarP negateV_14) (NormalB (VarE GHC.Num.negate)) []]
]
Run Code Online (Sandbox Code Playgroud)
从这里开始,看看如何使用组合器构造实例非常简单.另请注意,您可以将引号与引号表达式混合使用[| some code |] :: ExpQ,这通常对创建函数体很有用.