11

我正在为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
  |])

这很好用,但请注意,我只$t在牛津括号中拼接了一次。现在,派生VectorSpace实例的函数:

deriveScalarVectorSpace ts = concat <$> forM (map conT ts) (\t -> [d|    
    instance VectorSpace $t where type Scalar $t = $t; (*^) = (*)
  |])

但是,这很糟糕:

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
          { *^ = (*) } |]

t_ts错误中的和之间的区别t_tt告诉我,每次我拼接$t时 TH 都会创建一个新的、唯一的名称,当然,只有当这些类型相同时,定义才会起作用。

有没有办法用牛津括号获得我想要的行为,或者我是否必须退回到好的旧词法范围和Language.Haskell.TH组合器?我知道使用 CPP 可能会更容易,但我想借此机会学习一些 TH。

4

1 回答 1

5

我认为您将不得不使用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)) []]
]

从这里,很容易了解如何使用组合器构造实例。另请注意,您可以将引用与引用表达式 TH 混合使用[| some code |] :: ExpQ,这通常对创建函数体很有用。

于 2011-12-07T11:49:01.320 回答