Cómo capturar la variable de tipo en TemplateHaskell quote
Estoy tratando de escribir una instancia de Lift que eleve no solo el constructor, sino también sus variables de tipo. Por ejemplo, tome Proxy a. Necesito una Liftinstancia tal que, cuando lift (Proxy @Int)se empalme, GHC infiera correctamente que la expresión generada es un Proxy Int.
-- GHC should infer that x :: Proxy Int
x = $(TH.lift (Proxy @Int))
Probé esto:
instance Lift (Proxy a) where
lift _ = [|Proxy @a|]
x = $(TH.lift (Proxy @Int))
Parece que TH capturó ay no Intcomo se esperaba. No estoy seguro de que mas probar
/.../TH/Test.hs:15:7: error:
• The exact Name ‘a’ is not in scope
Probable cause: you used a unique Template Haskell name (NameU),
perhaps via newName, but did not bind it
If that's it, then -ddump-splices might be useful
Respuestas
template-haskell no parece proporcionar nada de eso. Pero puede haber una solución que pueda implementar desde cero. La idea es definir una clase para llevar una cotización que represente cada tipo:
class TLift a where
tlift :: Q Type
Por ejemplo:
instance TLift Int where
tlift = [t|Int|]
-- and so on
Luego, para definir una cotización con una aplicación tipo:
proxyQ :: forall a. TLift a => Q Exp
proxyQ = [|Proxy @( $(tlift @a) )|]
Una limitación aquí es que las TLiftinstancias solo pueden producir cotizaciones para tipos completamente concretos, no variables de tipo. Quizás la reflexión pueda solucionar eso.
Estoy bastante seguro de que esto no se puede hacer con una referencia directa al tipo para usar aplicaciones de tipo. Para cuando el código empalmado hace una referencia a, el ámbito en el que se definió ya no existe.
Pero creo que este es uno de los problemas conocidos que llevaron a la creación de la plantilla escrita Haskell. Y eso se puede usar para resolver esto. Tenga en cuenta que GHC 8.10 introdujo una entrada adicional en la clase liftTyped, que puede resolver esto directamente. Pero también proporciona la pista necesaria para usar esto en versiones anteriores de GHC, que es eso lift x === unTypeQ (liftTyped x).
A partir de eso, pude ver cómo escribir una instancia de Liftpara Proxy alas versiones de GHC mayores de 8,10.
instance Lift (Proxy a) where
lift x = unTypeQ (helper x)
where
helper :: Proxy b -> Q (TExp (Proxy b))
helper _ = [|| Proxy ||]
Para GHC 8.10, esto sería
instance Lift (Proxy a) where
liftTyped _ = [|| Proxy ||]
Honestamente, Liftaparentemente faltan un montón de casos. Este es solo uno de los más indirectos para escribir, al menos antes de GHC 8.10.