Usando la plantilla Haskell, ¿cómo puedo empalmar el mismo tipo en múltiples ubicaciones?

StackOverflow https://stackoverflow.com/questions/8410761

  •  29-10-2019
  •  | 
  •  

Pregunta

Estoy definiendo casos de clases de vector-espacio Para el Abre tipos, y para ahorrar mis músculos de escritura, quiero usar la plantilla Haskell para escribir un montón de instancias para mí.

Comencé pequeño definiendo la función para obtener instancias para 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
  |])

Esto funciona bien, pero tenga en cuenta que solo estoy empalmando $t Una vez en los soportes de Oxford. Ahora, la función para derivar VectorSpace instancias:

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

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

La diferencia entre t_ts y t_tt En el error me dice que está creando un nombre nuevo y único cada vez que empalme en $t, cuando, por supuesto, la definición solo funcionará si esos tipos son iguales.

¿Hay alguna manera de obtener el comportamiento que quiero con los soportes de oxford, o tendré que recurrir al buen alcance léxico y el Language.Haskell.TH combinadores? Sé que esto probablemente sería más fácil con el CPP, pero quiero aprovechar esta oportunidad para aprender algo de TH.

¿Fue útil?

Solución

Creo que tendrás que usar el Language.Haskell.TH combinadores. Ver los siguientes boletos:

Hacerlo es muy sencillo. Comenzaría con esto (ligeramente formateado)

*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)) []]
]

Desde aquí, es bastante simple ver cómo construir instancias usando los combinadores. También tenga en cuenta que puede mezclar citas con th, una expresión citada [| some code |] :: ExpQ, que a menudo es útil para crear cuerpos de funciones.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top