Un esempio di come un tipo di dati di questo tipo ci consente di abitare qualsiasi tipo viene fornito in Turner, D.A. (2004-07-28), Total Functional Programming, sez. 3.1, pagina 758 in Regola 2: Tipo di ricorsione deve essere covarianti "
Facciamo un esempio più elaborato utilizzando Haskell Inizieremo con una. 'Cattivo' ricorsivo tipo di dati
data Bad a = C (Bad a -> a)
.
e costruire il Y combinator da esso senza qualsiasi altra forma di ricorsione. Ciò significa che avere un tale tipo di dati permette di realizzare qualsiasi tipo di ricorsione, o abitare qualsiasi tipo per una ricorsione infinita.
Il combinatore Y nel lambda calcolo tipizzato è definito come
Y = λf.(λx.f (x x)) (λx.f (x x))
La chiave è che applichiamo x
a se stesso in x x
. Nelle lingue digitate questo non è direttamente possibile, perché non esiste un tipo valido x
. Ma il nostro tipo di dati Bad
permette questo modulo aggiunta/rimozione costruttore:
selfApp :: Bad a -> a
selfApp ([email protected](C x')) = x' x
Facendo x :: Bad a
, possiamo scartare suo costruttore e applicare la funzione all'interno di x
stessa. Una volta che sappiamo come fare questo, è facile costruire il Combinator 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
Nota che né selfApp
né yc
sono ricorsivo, non v'è alcuna chiamata ricorsiva di una funzione a se stesso. La ricorsione appare solo nel nostro tipo di dati ricorsivo.
Possiamo controllare che il combinatore costruito faccia effettivamente ciò che dovrebbe. Possiamo fare un ciclo infinito:
loop :: a
loop = yc id
o calcolare diciamo 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
Bella risposta. Mi piace questo approccio elegante con la sua spiegazione teorica (incorporando il calcolo lambda non tipizzato). Sarebbe possibile estenderlo in modo da dare una ricorsione arbitraria alla lingua in questione (diciamo Agda)? –
@ PetrPudlák Quindi, ho appena chiacchierato con i miei colleghi, che sono di gran lunga migliori teorici del tipo di me. Il consenso è che questo 'Bad' non darebbe origine a un termine di tipo' forall a. a' (che è quello che ti interessa davvero, la ricorsione è solo un mezzo per arrivarci). L'argomento dovrebbe essere così: è possibile costruire un modello teorico dell'insieme di Agda; quindi puoi aggiungere a quel modello un'interpretazione di 'Bad' come set di elementi singoli; dal momento che ci sono ancora tipi disabitati nel modello risultante, non c'è alcuna funzione che traduca i termini "Bad" in termini di loop di un altro tipo. –