Tipo di più metodi in un linguaggio statico funzionale all'oggetto

4

Attualmente sto lavorando per espandere le capacità del mio sistema di gestione del mio linguaggio di programmazione per animali domestici e mi sono imbattuto in un'area interessante in cui la mia ricerca sui sistemi di tipi di altre lingue non mi ha portato prima.

Come sfondo, il mio linguaggio è un linguaggio funzionale agli oggetti con tipi statici e inferenza di tipo parziale. Il linguaggio supporterà (ma non ancora) entrambe queste funzionalità:

  • funzioni il cui comportamento dipende dal tipo di runtime di tutti i loro argomenti (cioè multi-metodi)
  • tipi di unione, vale a dire che un valore può avere uno di molti tipi, i quali sono tracciati in modo trasparente in fase di esecuzione (simile a un tipo di somma ma senza bisogno di costruire esplicitamente o dichiarazione di quali tipi possono essere usati).

Data questa combinazione, il tipo di una funzione è interessante. Ad esempio, se ho un valore definito così:

v : union (int, string) = randomInt & 1 == 0 ? 1 : "hello"

Posso quindi applicarlo a qualsiasi funzione che accetta int o string per il suo primo argomento. Questo potrebbe significare una funzione che accetta un tipo di unione:

fn1 (p : union (int, string)) { p + 1; }

Ma potrebbe anche essere un multi-metodo definito per accettare entrambi i tipi:

fn2 (p : int) { "Integer: " + p; },
    (p : string) { p.length }

Mentre potrei descrivere i tipi di queste funzioni usando un'unione dei loro parametri e risultato, ad esempio (usando una notazione Haskell appropriatamente estesa):

fn1 :: union (int, string) -> union (int, string)

(e lo stesso tipo di fn2) questo perde informazioni, cioè il fatto che fn1 mantiene il tipo del suo argomento mentre fn2 scambia tra i due tipi.

Qualcuno ha visto una lingua che tiene traccia dei tipi di funzione in questo modo? In tal caso, che tipo di notazione utilizza per la combinazione di diversi argomenti e tipi di risultati e le loro interazioni? C'è un nome per il tipo di funzione di cui stiamo parlando? E vale la pena farlo, o dovrei semplicemente permettere che le informazioni vengano perse?

Un altro pensiero interessante: la funzione fn2 può essere vista come una combinazione di due funzioni, una di tipo int- > string e l'altro string- > Int. Il mio primo pensiero è che il tipo di funzione è un'unione di questi, ma questo è ovviamente sbagliato - il mio pensiero attuale è che è in realtà un doppio del tipo di unione, analogo al modo in cui un tipo di prodotto si riferisce a un tipo di somma. Qualche altra idea su questo? Sono sulla buona strada? Esiste un nome standard per questo tipo?

    
posta Jules 08.06.2016 - 12:19
fonte

2 risposte

2

Qualche spunto di riflessione. I sovraccarichi sono tradizionalmente fatti in fase di compilazione. Stai parlando di fare la differenziazione in fase di esecuzione dei sovraccarichi, o in altre parole, introducendo una distribuzione runtime alla selezione del sovraccarico.

Ci sono molte cose interessanti. Innanzitutto, la maggior parte delle lingue con sistemi di tipi statici che eseguono la distribuzione runtime (ad esempio per i metodi virtuali o di interfaccia) hanno un modo per garantire che la spedizione non comporti un errore metodo non trovato. Ad esempio, in C # dichiariamo che una classe implementa un'interfaccia, e quindi la classe deve fornire un'implementazione per ciascuno dei metodi dell'interfaccia, altrimenti si tratta di un errore in fase di compilazione.

Quello a cui sto arrivando è il meccanismo con cui il f2 è correlato al tipo di unione. Non è dichiarato come soddisfacente il sindacato, ma piuttosto semplicemente il supporto ai singoli membri. Una domanda sarebbe: se f2 ha dichiarato solo int->string version , dove e quando verrebbe un errore per l'utilizzo / invocazione f2 ( union ); . Ho potuto vedere che in fase di compilazione è possibile emettere un errore al punto dell'invocazione di f2 che cita che non fornisce metodi sufficienti per coprire l'unione. (Un'altra opzione potrebbe essere che f2 dichiari il suo intento di coprire l'unione, quindi potresti emettere un errore di tempo di compilazione alla definizione di f2 piuttosto che a tale uso di f2 .)

Un altro punto da sottolineare è che l'invocazione di con un'espressione di unione, f2 ( union ) , è in un certo senso in grado di aprire l'unione per fare la spedizione. Qui viene in mente la digitazione nominale e quella strutturale, anche se forse questo non è completamente il concetto corretto. È quasi come se ci fosse una selezione membro del valore, come in qualcosa come f2 ( union.value ); o f2 ( valueOf ( union ); . (La tua lingua deve avere un modo per ottenere (uno dei) il valore (i) dell'unione.) Questo in contrasto con un po 'di f2 che in realtà accetta il tipo di sindacato completo, vale a dire che forse si apre il sindacato viene eseguito solo quando non è disponibile f2 del tipo di unione effettivo?

Si noti che una classe base astratta con due sottoclassi riflette la nozione di unioni (taggate). Un metodo (1) può accettare la classe base come parametro, che è potenzialmente una sottoclasse. Due altri metodi potrebbero accettare sottoclassi specifiche. E l'implementazione del metodo (1) potrebbe scegliere tra gli altri due metodi basati sul vero tipo della sottoclasse (questo, manualmente, è l'invio di cui si sta parlando internamente e automaticamente).

Sul tuo ultimo punto vedrei il tipo di f2 come

    union ( int->string, string->int )

anziché union ( int, string ) -> union ( string, int ) , che hai notato come problematico, poiché introduce opzioni non fornite come string->string e int->int .

Forse l'unione di due funzioni non era elegante come speravate. Le alternative potrebbero utilizzare parametri di tipo con espressioni qualificanti complesse, come in

    T -> S where (T=string and S=int) or (T=int and S=string);

Solo qualche spunto di riflessione ...

    
risposta data 08.06.2016 - 18:54
fonte
1

Un approccio molto simile è comune nei linguaggi orientati agli oggetti. Lì, si chiama sovraccarico di funzione e significa che hai più funzioni distinte con lo stesso nome, ma diverse firme.

Questi linguaggi non assegnano il tipo al gruppo di funzioni con lo stesso nome, ma penso che un modo per vederlo è che è una tupla (o, più formalmente, prodotto ) delle funzioni.

    
risposta data 08.06.2016 - 17:56
fonte

Leggi altre domande sui tag