Un ejemplo de cómo este tipo de datos nos permite habitar en cualquier tipo se da en Turner, D.A. (2004-07-28), Total Functional Programming, secc. 3.1, página 758 en Regla 2: Tipo de recursión debe ser covariante "
Hagamos un ejemplo más elaborado usando Haskell Vamos a empezar con una. "Malo" tipo de datos recursiva
data Bad a = C (Bad a -> a)
.
y construir el Y combinator de ella sin cualquier otra forma de recursión. Esto significa que tiene un tipo de tales datos nos permite construir cualquier tipo de recursión, o habitar cualquier tipo por una recursión infinita.
El combinador Y en el cálculo lambda sin tipo se define como
Y = λf.(λx.f (x x)) (λx.f (x x))
La clave para esto es que aplicamos x
a sí mismo en x x
. En los idiomas tipados, esto no es directamente posible, porque no existe un tipo válido x
que pueda tener. Pero nuestra Bad
tipo de datos permite que este módulo añadir/eliminar el constructor:
selfApp :: Bad a -> a
selfApp ([email protected](C x')) = x' x
Tomando x :: Bad a
, podemos desenvolver su constructor y aplicar la función dentro de x
sí. Una vez que sabemos cómo hacer esto, es fácil de construir el combinador Y:
yc :: (a -> a) -> a
yc f = let fxx = C (\x -> f (selfApp x)) -- this is the λx.f (x x) part of Y
in selfApp fxx
Tenga en cuenta que ni selfApp
ni yc
son recursivos, no hay una llamada recursiva de una función a sí mismo. La recursividad aparece solo en nuestro tipo de datos recursivos.
Podemos comprobar que el combinador fabricado realmente hace lo que debería. Podemos hacer un bucle infinito:
loop :: a
loop = yc id
o calcular digamos GCD:
gcd' :: Int -> Int -> Int
gcd' = yc gcd0
where
gcd0 :: (Int -> Int -> Int) -> (Int -> Int -> Int)
gcd0 rec a b | c == 0 = b
| otherwise = rec b c
where c = a `mod` b
Nice answer. Me gusta este enfoque elegante con su explicación teórica (incrustación del cálculo lambda sin tipo). ¿Sería posible extenderlo para que brinde recursión arbitraria al idioma en cuestión (Agda digamos)? –
@ PetrPudlák Por lo tanto, acabo de hablar con mis compañeros de oficina, que son mucho mejores teóricos tipo que yo. El consenso es que este 'Malo 'no daría lugar a un término de tipo' por todo a. a' (que es lo que realmente te importa, la recursividad es solo un medio para llegar allí). El argumento sería así: puedes construir un modelo teórico de conjuntos de Agda; entonces puede agregar a ese modelo una interpretación de 'Malo 'como un conjunto de un solo elemento; ya que todavía hay tipos deshabitados en el modelo resultante, no hay una función que traduzca los términos 'Malos 'de bucle en términos de bucle de otro tipo. –