tutorial mexico logo empresa ejemplos descargar constructora company haskell

mexico - haskell logo



¿Qué son los GADTs? (2)

Estaba leyendo la página de GADT para dummies en el Wiki de Haskell, y todavía no entiendo cómo y por qué deberían usarse. El autor proporcionó un ejemplo motivador:

data T a where D1 :: Int -> T String D2 :: T Bool D3 :: (a,a) -> T [a]

¿Qué hace exactamente este código y por qué es útil?

Si esta pregunta es demasiado vaga, tal vez una pregunta relacionada es: ¿Se pueden usar GADT para implementar funciones de miembro?


Digamos que quieres modelar una bolsa de frutas. Esta bolsa puede tener manzana o naranjas. Así que como buen haskeller definís:

data Bag = Oranges Int | Apples Int

Eso se ve bonito. Veamos ese tipo de Bag solo sin mirar a los constructores de datos. ¿El tipo de Bag solo le da una indicación de si es una bolsa naranja o una bolsa de manzanas? Bueno, no de forma estática, me refiero a que en el tiempo de ejecución una función podría coincidir con el valor del tipo de Bag para detectar si son naranjas o manzanas, pero no sería bueno aplicar esto en el tiempo de compilación / verificación de tipo en sí, de modo que una función Solo funciona con bolsa de manzanas, no se puede pasar bolsa de naranjas en absoluto.

Aquí es donde GADT puede ayudarnos, básicamente nos permite ser más precisos sobre nuestros tipos:

data Orange = ... data Apple = .... data Bag a where OrangeBag :: [Orange] -> Bag [Orange] AppleBag :: [Apple] -> Bag [Apple]

Ahora puedo definir una función que solo funciona con bolsa de manzanas.

giveMeApples :: Bag [Apple] -> ...


Los GADT le permiten tener sus tipos para contener más información sobre los valores que representan. Lo hacen estirando data declaraciones de data Haskell un poco hacia las familias de tipo inductivo en un lenguaje tipificado de forma dependiente.

El ejemplo por excelencia se escribe Sintaxis abstracta de orden superior representado como un GADT.

{-# LANGUAGE GADTs #-} {-# LANGUAGE TypeOperators #-} -- Not needed, just for convenience of (:@) below module HOAS where data Exp a where Lam :: (Exp s -> Exp t) -> Exp (s -> t) (:@) :: Exp (s -> t) -> Exp s -> Exp t Con :: a -> Exp a intp :: Exp a -> a intp (Con a) = a intp (Lam f) = intp . f . Con intp (fun :@ arg) = intp fun (intp arg)

En este ejemplo, Exp es un GADT. Tenga en cuenta que el constructor Con es muy normal, pero los constructores App y Lam introducen nuevas variables de tipo con bastante libertad. Estas son variables de tipo cuantificadas existencialmente y representan relaciones bastante complejas entre los diferentes argumentos de Lam y App .

En particular, se aseguran de que cualquier Exp pueda interpretarse como una expresión de Haskell bien escrita. Sin usar GADT, deberíamos usar los tipos de suma para representar los valores en nuestros términos y manejar los errores de tipo.

>>> intp $ Con (+1) :@ Con 1 2 >>> Con (+1) :@ Con ''a'' <interactive>:1:11: Warning: No instance for (Num Char) arising from a use of `+'' Possible fix: add an instance declaration for (Num Char) In the first argument of `Con'', namely `(+ 1)'' In the first argument of `App'', namely `(Con (+ 1))'' In the expression: App (Con (+ 1)) (Con ''a'') >>> let konst = Lam $ /x -> Lam $ /y -> x >>> :t konst konst :: Exp (t -> s -> t) >>> :t intp $ konst :@ Con "first" intp $ konst :@ Con "first" :: s -> [Char] >>> intp $ konst :@ Con "first" :@ Con "second" "first"