Eventi negativi "custoditi" nella definizione di tipi induttivi, sempre cattivi?


11

So come alcuni eventi negativi possano essere definitivamente cattivi:

data False

data Bad a = C (Bad a -> a)

selfApp :: Bad a -> a
selfApp (x@(C x')) = x' x

yc :: (a -> a) -> a
yc f = selfApp $ C (\x -> f (selfApp x))

false :: False
false = yc id

Tuttavia, non sono sicuro se:

  • tutti i tipi induttivi con occorrenze negative possono sbagliare;

  • in tal caso, esiste un modo meccanico noto per farlo;

Ad esempio, ho combattuto cercando di far andare male questo tipo:

type Not a = a -> False

data Bad2 a = C2 (Bad2 (Not a) -> a)

Qualsiasi suggerimento alla letteratura su questo argomento sarebbe apprezzato.


1
Questo è Coq? Haskell? Teoria dello pseudo-tipo? Cosa intendi con "vai storto"?
Dave Clarke,

@DaveClarke Siamo spiacenti, il codice è Haskell, ma la preoccupazione riguarda più le lingue come Coq o Agda in cui sono vietati gli eventi negativi. Per "sbagliare" intendo essere in grado di scrivere un termine che diverge, potendo così abitare False come ho fatto nel mio esempio in Haskell.
Ptival,

Risposte:


10

Il motivo del divieto di eventi negativi può essere compreso per analogia con il teorema di Knaster-Tarski. Questo teorema dice questo

se è un reticolo completo e f : L L è una funzione monotona su L , anche l'insieme dei punti fissi di f è un reticolo completo. In particolare, esiste un punto fisso minimo μ f e un punto fisso massimo ν f .Lf:LLLfμfνf

Nella teoria dei modelli tradizionale, i reticoli possono essere visti come proposizioni e la relazione d'ordine p q può essere intesa come coinvolgimento (cioè che la verità di q è implicata dalla verità di p ).Lpqqp

Quando passiamo dalla teoria dei modelli alla teoria delle prove, i reticoli si generalizzano in categorie. Tipi possono essere visti come gli oggetti di una categoria , e una mappa e : P Q rappresenta una prova che Q può essere derivata da Q .Ce:PQQQ

Quando proviamo a interpretare i tipi definiti da equazioni ricorsive, ee, , la cosa ovvia da fare è cercare una generalizzazione del teorema di Knaster-Tarski. Così, invece di una funzione monotona su un reticolo, sappiamo che vogliamo unfuntore F : CC , che invia gli oggetti agli oggetti, ma generalizza la condizione di monotonia in modo che ogni mappa e : P Q ottiene una mappa F ( e ) : F ( P ) F ( Q ) (con le condizioni di coerenza che F invia identità alle identità e conserva le composizioni in modo che FN=μα.1+α F:CCe:PQF(e):F(P)F(Q)F ).F(gf)=F(g)F(f)

Quindi, se si desidera un tipo di dati induttivo , è inoltre necessario fornire un'azione funzionale in base ai termini per l'operatore di tipo F per essere certi che esista il punto fisso desiderato. La rigorosa condizione di positività in Agda e Coq è unacondizionesintatticache implica questovincolosemantico. In parole povere, si dice che se si crea un operatore di tipo da somme e prodotti, allora si può sempre cucinare l'azione funzionale, e quindi qualsiasi tipo formato in questo modo dovrebbe avere un punto fisso.μα.F(α)F

Nelle lingue tipizzate in modo dipendente, hai anche tipi indicizzati e parametrizzati, quindi il tuo vero compito è più complicato. Bob Atkey (che ha scritto questo blog qui e qui ) mi dice che un buon posto per cercare la storia è:

Come osserva Andrej, fondamentalmente se un evento negativo va bene o no dipende da quale sia il tuo modello di teoria dei tipi. Fondamentalmente, quando hai una definizione ricorsiva, stai cercando un punto fisso e ci sono molti teoremi a punto fisso in matematica.

Uno di cui personalmente ho fatto molto uso è il teorema del punto fisso di Banach, che dice che se si ha una funzione strettamente contrattuale su uno spazio metrico, allora ha un punto fisso unico. Questa idea è stata introdotta in semantica da (IIRC) Maurice Nivat, ed è stata ampiamente studiata da America e Rutten, ed è stata recentemente collegata da Birkedal e dai suoi collaboratori a una popolare tecnica operativa chiamata "indicizzazione a passi".

Ciò dà origine a teorie di tipo in cui sono consentite occorrenze negative in tipi ricorsivi, ma solo quando si verificano occorrenze negative sotto uno speciale costruttore di tipo "protezione". Questa idea è stata introdotta da Hiroshi Nakano e la connessione con il teorema di Banach è stata fatta sia da me che da Nick Benton, nonché da Lars Birkedal e dai suoi coautori.


7

A volte puoi risolvere equazioni ricorsive "per fortuna".

UN(UN)UN.
  1. UNUN

    UNUN1.
    1
  2. UN()1

Conclusione: esistono due soluzioni, il tipo vuoto (che hai chiamato False) e il tipo di unità ().

UN(UN2)2,
data Cow a = Moo ((a -> Bool) -> Bool)

UN22UNUN22UN

N22N.
2N22NInteger(Integer -> Bool) -> Bool

3

È difficile aggiungere qualcosa alle spiegazioni di Andrej o Neel, ma ci proverò. Proverò ad affrontare il punto di vista sintattico, piuttosto che cercare di scoprire la semantica sottostante, perché la spiegazione è più elementare e posso dare una risposta più semplice alla tua domanda.

λ

Il riferimento cruciale è il seguente:

Mendler, N. (1991). Tipi induttivi e vincoli di tipo nel calcolo lambda del secondo ordine. Non ho trovato un riferimento online, temo. Le dichiarazioni e le prove possono tuttavia essere trovate nella tesi di dottorato di Nax (una lettura altamente raccomandata!).

Bun'd

Bun'd=Bun'dUN

UN

λX:Bun'd.X X:Bun'dUN

e così

(λX:Bun'd.X X) (λX:Bun'd.X X):UN

Bun'd=F(Bun'd)
F(X)XF(X)

Ovviamente stai lavorando non con tipi definiti equazionalmente ma con costruttori , cioè hai

data Bad = Pack (Bad -> A)

piuttosto che una rigorosa uguaglianza. Comunque puoi definire

unpack :: Bad -> (Bad -> A)
unpack (Pack f) = f

che è sufficiente affinché questo risultato continui a contenere:

 (\x:Bad -> unpack x x) (Pack (\x:Bad -> unpack x x))

UN


Nel tuo secondo esempio, le cose sono un po 'più complicate, dato che hai qualcosa sulla falsariga di

Bun'd=Bun'd'UN

Bun'd'Bun'dBun'd un'Bun'd (Not un')

type Not a = a -> False

con

data Not a = Not a

Sarebbe facilmente risolvibile se Haskell consentisse tali definizioni di tipo:

type Acc = Not Acc

In questo caso, è possibile creare un combinatore di loop esattamente come prima. Ho il sospetto che tu possa trasportare una costruzione simile (ma più complessa) usando

data Acc = D (Not Acc)

Il problema qui è quello di costruire un isomorfismo

Bad Acc <-> Bad (Not Acc)

devi affrontare una varianza mista.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.