Usando Phantom Types
Revisando un poco la implementación de Haskell (GHC) me encuentro con que usan phantom types de una manera bastante interesante (y compleja también). Esto me da una idea de que podríamos aspirar a tener algo similar pero bastante menos complejo.
¿Qué hacen en la implementación de Haskell? (a muy grandes rasgos)
Definen tipos
type GhcPs -- Parseo OK
type GhcRn -- Renombre OK
type GhcTc -- TypeChequeo OK
que representan distintas etapas de revisión o actualización de un árbol sintáctico. Además definen el tipo de las expresiones/declaraciones (de nuevo a grandes rasgos porque está bastante más desparramada la definición de este tipo de dato) como
-- | A Haskell Declaration
data HsDecl p = ...
acá la p
(o pass
, como también se lo encuentra en el código) no aparece después en ningún constructor (solo se propaga para otras estructuras) y esto transforma al tipo en phantom type.
La utilidad ahora es que después de cada etapa de chequeo la función correspondiente al chequeo va a retornar algo de tipo HsDecl GhcPs
(expresión sólo parseada), HsDecl GhcRn
(expresión parseada y con renombre de variables) y HsDecl GhcTC
(expresión parseada, con renombre de varibles y typechequeada). Mucho más interesante es el tipo de los argumentos que toma cada una de estas funciones. Siguiendo con el ejemplo a enormes rasgos la función de typechequeo asume que a la expresión parseada ya se le realizaron determinados renombres. Esta secuencialidad la podemos asegurar entonces a nivel de tipos definiendo
parser :: String -> HsDecl GhcPs
rename :: HsDecl GhcPs -> HsDecl GhcRn
typecheck :: HsDecl GhcRn -> HsDecl GhcTc
Claramente si intentamos typechequear una expresión recién parseada (con tipo HsDecl GhcPs
) vamos a tener un error de tipado; esto nos ayuda a dar alguna garantía más sobre precondiciones que le imponemos a la etapa de chequeo de tipos.
¿Qué haríamos nosotros?
Bueno, hay que pensarlo bien. Si nos gusta esta idea entonces necesitaríamos definir las etapas y catalogar a cada chequeo según esa etapa.