Come verificare / dimostrare l'ortogonalità di un linguaggio di programmazione?

10

Conosco il concetto di ortogonalità, ma dal punto di vista del linguaggio di programmazione, c'è un modo per verificarlo / dimostrarlo?

Ad esempio in C #, si può usare public o static per una firma del metodo. Puoi usare entrambi o entrambi e non interferirebbero tra loro, quindi sono ortogonali tra loro, giusto?

La mia domanda è: come faccio a fare il resto delle funzionalità, in particolare quelle che non sono correlate tra loro?

Tutte le funzioni devono coesistere / raggrupparsi insieme?

Esiste un linguaggio di programmazione al 100% ortogonale?

    
posta Joan Venge 03.01.2012 - 23:13
fonte

6 risposte

4

Non sono sicuro che l'ortogonalità possa servire come metrica utile o valida in caso di linguaggi di ordine generale di uso generale come C #, poiché richiede la distinzione di "operazioni" e "operandi" - le piccole parti della lingua che non sono facilmente distinguibili in linguaggi di alto livello come C #.

La mia comprensione dell'ortogonalità si basa sul linguaggio Assembler in cui ortogonalità dell'insieme di istruzioni di una determinata CPU o microcontrollore indica se ci sono dei vincoli sulle operazioni eseguite da questa CPU o controller a seconda dei tipi di dati. Nei primi tempi questo era importante perché non tutte le CPU supportavano operazioni su numeri frazionari o numeri di lunghezza diversa ecc.

In questo senso preferirei verificare l'ortogonalità della Common Intermediate Language usando il linguaggio Stack Machine come target per Compilatore C #, non C # stesso.

Se sei veramente interessato all'ortogonalità di C # e non mi sto sbagliando qui (per uno scopo qualsiasi) suggerirei di guardare verso alcuni genetica algoritmi di programmazione . È possibile utilizzare quelli per generare diversi programmi dal set di parole chiave dato (anche quelli privi di significato) e si può solo verificare automaticamente se sono compilabili. Ciò ti aiuterà a vedere automaticamente quali elementi della lingua possono essere combinati insieme e ricavare alcuni aspetti della tua metrica di ortogonalità.

    
risposta data 04.01.2012 - 00:55
fonte
6

Il termine "ortogonalità" è un termine per laico per una precisa nozione matematica: i termini della lingua formano un'algebra iniziale (cercalo in Wikipedia).

In pratica significa "c'è una corrispondenza di 1-1 tra sintassi e significato". Questo significa che c'è esattamente un modo per esprimere le cose e, se riesci a mettere un po 'di espressione in un luogo particolare, puoi inserire anche qualsiasi altra espressione.

Un altro modo di pensare "ortogonale" è che la sintassi obbedisce al principio di sostituzione. Ad esempio, se si dispone di un'istruzione con uno slot per un'espressione, è possibile inserire qualsiasi espressione e il risultato è ancora un programma sintatticamente valido. Inoltre, se sostituisci

Voglio sottolineare che "significato" non implica risultato computazionale. Chiaramente, 1 + 2 e 2 + 1 sono entrambi uguali a 3. Tuttavia i termini sono distinti e implicano un calcolo diverso anche se ha lo stesso risultato. Il significato è diverso, così come due algoritmi di ordinamento sono diversi.

Potresti aver sentito parlare di "abstract syntax tree" (AST). La parola "astratto" qui significa precisamente "ortogonale". Tecnicamente la maggior parte degli AST non sono in realtà astratti!

Forse hai sentito parlare del linguaggio di programmazione "C"? La notazione di tipo C non è astratta. Prendere in considerazione:

int f(int);

Quindi ecco una dichiarazione di funzione che restituisce il tipo int . Il tipo di puntatore a questa funzione è dato da:

int (*)(int)

Nota, non puoi scrivere il tipo di funzione! La notazione di tipo C fa schifo! Non è astratto Non è ortogonale. Ora, supponiamo di voler creare una funzione che accetti il tipo precedente invece di int:

int (*) ( int (*)(int) )

Tutto ok .. ma .. cosa succede se invece vogliamo restituirlo:

int (*)(int) (*) (int)

Woops! Non valido. Consente di aggiungere parens:

(int (*)(int)) (*) (int)

Woops! Neanche questo funziona. Dobbiamo farlo (è l'unico modo!):

typedef int (intintfunc*) (int);
intintfunc (*)(int)

Ora va bene, ma dover usare un typedef qui è brutto. C fa schifo. Non è astratto Non è ortogonale. Ecco come lo fai in ML, che è:

 int -> (int -> int)

Condanniamo C al livello di sintassi.

Ok, ora lascia flog C ++. Possiamo correggere la stupidità di cui sopra con i modelli e ottenere una notazione simile a ML (più o meno):

fun<int, int>
fun< fun<int,int>, int>

ma il sistema di tipi effettivo è fondamentalmente imperfetto dai riferimenti: se T è un tipo, allora T& è un tipo? La risposta è waffly: al livello di sintassi, se hai un tipo U = T & amp ;, quindi U & è permesso ma significa solo T & amp ;: un riferimento a un riferimento è il riferimento originale. Questo fa schifo! Rompe semanticamente il requisito di unicità. Peggio ancora: T & & non è permesso sintatticamente: questo rompe il principio di sostituzione. Quindi i riferimenti C ++ interrompono l'ortogonalità in due modi diversi, a seconda del tempo di associazione (analisi o analisi del tipo). Se vuoi capire come farlo bene .. non c'è problema con i puntatori!

Quasi nessuna lingua reale è ortogonale. Persino Scheme, che pretende una grande chiarezza di espressione, non lo è. Tuttavia, molte buone lingue possono essere giudicate "ragionevolmente vicine alla caratteristica ortogonale" e questa è una buona raccomandazione per una lingua, applicata sia alla sintassi che alla semantica sottostante.

    
risposta data 05.01.2012 - 02:01
fonte
4

Dimostrare che l'ortogonalità si sta dimostrando negativa. Significa che non hai nessun costrutto che non sia ortogonale, il che significa che è molto più semplice dimostrare che non è ortogonale di quanto lo sia.

In pratica, molte persone parlano di ortogonalità dei linguaggi di programmazione in termini di gradi piuttosto che essere completamente ortogonali o meno. Quando la conoscenza di fare qualcosa in un contesto si traduce in un altro contesto e "fa ciò che ti aspetti", si dice che la lingua sia più ortogonale. LISP è considerato altamente ortogonale perché tutto è una lista, ma non penso che si possa dire che è al 100% ortogonale a causa di alcune ridondanze che ne facilitano l'uso. Il C ++ è considerato non molto ortogonale perché ci sono molti piccoli "trucchi" in cui non funziona nel modo in cui pensi che sarebbe.

    
risposta data 04.01.2012 - 00:21
fonte
3

Attenzione, non so nulla su questo argomento.

Una rapida occhiata a Wikipedia sembra indicare che l'ortogonalità è principalmente rivolta a schemi di progettazione e progettazione di sistemi. In termini di linguaggi di programmazione, la voce indica che i set di istruzioni sono ortogonali se c'è una sola istruzione per ogni azione possibile, o piuttosto, nessuna istruzione si sovrappone a un'altra.

Per C #, immagino che sia ortogonale, in quanto la maggior parte dei trucchi della sintassi ( foreach viene in mente) sono semplicemente front-end per le versioni appositamente formate del costrutto di base ( foreach diventa for loops ). Nel complesso, il linguaggio supporta veramente solo le cose in un modo, anche se lo zucchero sintattico fornisce ulteriori modi per farlo. Infine, compila tutti fino a MSIL (o come si chiamino in questi giorni) e MSIL è probabile ortogonale.

Se si fa l'avvertenza che la sostanza sintattica dello zucchero è essenzialmente un "involucro" intorno a farlo, la "via difficile" è possibile analizzare le varie caratteristiche della lingua, omettere lo zucchero e vedere se ci sono dei costrutti che si sovrappongono veramente. In caso contrario, immagino che potresti dichiarare la lingua ortogonale.

I miei due centesimi.

    
risposta data 03.01.2012 - 23:30
fonte
2

My question is, how do I go about the rest of features, particularly features that are not related to each other?

Continui a fare quello che stai facendo, elencando tutte le combinazioni che funzionano o sono vietate.

Questo è tutto. È piuttosto doloroso da fare.

Do all the features have to coexist/stack together?

Se le tutte funzioni possono essere partizionate in sottoinsiemi disgiunti che non interferiscono tra loro, allora sicuramente, tutti sarebbe ragionevole.

Tutte le strutture dati funzionano con tutti i tipi primitivi. Tutti gli operatori di espressione funzionano con tutti i tipi. Quelle sono definizioni comuni di ortogonalità. Ma potresti volere di più (o meno)

A volte, tuttavia, ci sono casi speciali a causa di sistemi operativi o librerie legacy che non sono ortogonali.

Inoltre, alcuni tipi non sono davvero molto conformabili. Ad esempio Python consente di confrontare due oggetti del dizionario per "ordinare". Ma non c'è quasi nessuna definizione ragionevole di "ordinare" tra i dizionari. Python ne definisce uno, ma è piuttosto discutibile. Questo caso speciale rende i dizionari fallire un test di ortogonalità?

Quanto ortogonale è "abbastanza ortogonale"? Che cosa tu devi vedere per essere felice del grado di ortogonalità nella tua lingua.

    
risposta data 04.01.2012 - 00:15
fonte
2

L'elenco delle funzionalità non orientali è effettivamente lungo nella maggior parte dei linguaggi di programmazione, ad es.

  • il conflitto tra classi anonime e java reflection
  • conflitto di cancellazione generico e di tipo con java reflection
  • Gli array
  • sono in qualche modo diversi dagli altri oggetti, a causa del loro tipo speciale, anche se sono oggetti.
  • I metodi
  • statici e istanze non sono gli stessi, ad es. non puoi sovrascrivere un metodo statico
  • le classi annidate sono un ripensamento
  • impatto della digitazione dinamica rispetto a quella statica sulla strategia di invio messaggi (vedi ad esempio questo edge case in C #)
  • ecc.

Questi sono solo alcuni che mi vengono in mente, ma ce ne sono molti altri, e anche in altre lingue.

È difficile garantire che non ci siano interferenze sottili tra le funzionalità del linguaggio. Una cicatrice. Hoare indica nel suo articolo "Suggerimenti sulla programmazione del linguaggio di programmazione":

Part of language design consists of innovation. This activity leads to new language features in isolation. The most difficult part of language design lies in integration: selecting a limited set of language features and polishing them until the result is a consistent simple framework that has no more rough edges.

Probabilmente, una buona mossa per aumentare l'ortogonalità è unificare i concetti (che va nella direzione della risposta di @ karl-bielfeld). Se tutto è, diciamo una lista o un oggetto, è probabile che ci saranno meno conflitti. O invece di avere una classe annidata e dopo aver pensato, rendila una caratteristica fondamentale.

La maggior parte dei documenti sui linguaggi di programmazione dimostra alcune proprietà del linguaggio (ad esempio, il tipo di suono) su un sottoinsieme (un "nucleo") del linguaggio che è formalizzato. Qui dovremmo fare il contrario, provare che tutte le caratteristiche compongono in modo sicuro. Inoltre, ciò significa che è necessario definire cosa significa "comporre". Significa "correre"? (In questo caso, il collegamento sopra al caso limite con digitazione dinamica e statica è sicuro). Significa essere "sicuro"? Significa essere prevedibili dal punto di vista degli sviluppatori?

Tutto ciò è molto interessante - ma anche molto impegnativo.

    
risposta data 06.01.2012 - 10:35
fonte

Leggi altre domande sui tag