En utilisant le modèle Haskell, comment puis-je épisser le même type dans plusieurs emplacements?
-
29-10-2019 - |
Question
Je définis les instances de cours de espace vectoriel pour le Opengl Types, et pour épargner mes muscles de frappe, je veux utiliser le modèle Haskell pour écrire un tas des instances pour moi.
J'ai commencé petit en définissant la fonction pour dériver des instances pour 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
|])
Cela fonctionne bien, mais notez que je ne fais que l'épissage $t
Une fois entre les supports d'Oxford. Maintenant, la fonction à dériver VectorSpace
instances:
deriveScalarVectorSpace ts = concat <$> forM (map conT ts) (\t -> [d|
instance VectorSpace $t where type Scalar $t = $t; (*^) = (*)
|])
Mais, ce 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 différence entre t_ts
et t_tt
dans l'erreur me dit que cela crée un nouveau nom unique à chaque fois que j'épisse $t
, quand bien sûr, la définition ne fonctionnera que si ces types sont les mêmes.
Existe-t-il un moyen d'obtenir le comportement que je veux avec les supports d'Oxford, ou devrai-je retomber sur la bonne vieille portée lexicale et la Language.Haskell.TH
Combinateurs? Je sais que ce serait probablement plus facile avec le CPP, mais je veux profiter de cette occasion pour en apprendre un.
La solution
Je pense que vous devrez utiliser le Language.Haskell.TH
Combinateurs. Voir les billets suivants:
Cela est très simple. Je commencerais par cela (légèrement formaté)
*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)) []]
]
De là, il est assez simple de voir comment construire des instances à l'aide des combinateurs. Notez également que vous pouvez mélanger la citation avec TH, une expression citée [| some code |] :: ExpQ
, ce qui est souvent utile pour créer des corps de fonction.