Используя Template Haskell, как я могу разделить один и тот же тип на несколько мест? - PullRequest
11 голосов
/ 07 декабря 2011

Я определяю экземпляры классов из vector-space для типов 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; (*^) = (*)
  |])

Но, это 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
          { *^ = (*) } |]

Разница между t_ts и t_tt в ошибке говорит мне, что TH создает новое уникальное имя каждый раз, когда я склеиваю в $t, когда, конечно, определение будет работать, только если эти типы одинаковы ,

Есть ли способ получить поведение, которое я хочу, с помощью оксфордских скобок, или мне придется вернуться к старому доброму лексическому объему и комбинаторам Language.Haskell.TH? Я знаю, что, возможно, было бы легче с CPP, но я хочу воспользоваться этой возможностью, чтобы изучить некоторые TH.

1 Ответ

5 голосов
/ 07 декабря 2011

Я думаю, вам придется использовать комбинаторы 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, которое часто полезно для создания тел функций.

...