Lisp ha ancora qualche caratteristica speciale che NON è stata adottata da altri linguaggi di programmazione?


35

Lisp ha ancora qualche caratteristica speciale che NON è stata adottata da altri linguaggi di programmazione?

Per Lisp intendo tutti i linguaggi di programmazione Lisp nel loro insieme. Mi è stato detto quanto sia sorprendente Lisp e sappia che molte lingue sono state ispirate da Lisp. Ma Lisp ha ancora qualche caratteristica di design esclusivo che non può essere fatta in nessun'altra lingua?

Il motivo per cui ho posto la domanda è che di recente, essendo un programmatore dilettante, ho iniziato a imparare Clojure solo per divertimento, e il risultato è che ho trovato molti post e commenti relativi a Lisp, dicendo solo una cosa: "Lisp è unico ", ma altri linguaggi di programmazione moderni hanno già adottato e rubato molte idee da Lisp, come i condizionali, la ricorsione e la funzione di cittadino di prima classe. E anche la metaprogrammazione può essere fatta in molte lingue.

Mi sono perso qualcosa ed è "Lisp ancora diverso"?

O sono fortunato perché altre lingue moderne hanno rubato tutte le parti buone di Lisp in modo che non sia necessario scavare nel mondo delle parentesi Lisp , e "Lisp era diverso".


3
Condividere la tua ricerca aiuta tutti. Dicci cosa hai provato e perché non ha soddisfatto le tue esigenze. Ciò dimostra che hai impiegato del tempo per cercare di aiutarti, ci salva dal ribadire risposte ovvie e soprattutto ti aiuta a ottenere una risposta più specifica e pertinente. Vedi anche Come chiedere
moscerino

3
@gnat Thx per un consiglio, e ho aggiornato la mia domanda :)
iceX

10
Un problema è che, una volta che una lingua ha un certo sottoinsieme di funzionalità di Lisp (ad esempio, espressioni S e macro), le persone affermano che è un Lisp. Il che ovviamente ha la conseguenza che (secondo queste persone) nessun linguaggio non Lisp può avere queste caratteristiche.

9
Immagino che non ci sia altra lingua in cui le parentesi tonde sono l'unica forma di raggruppamento per tutto :-)
Doc Brown

Lisp come famiglia potrebbe non essere terribilmente unico, ma molti dialetti lisp (Racket, CL, Scheme, Clojure) offrono ancora molte funzioni utili / uniche.
Daniel Gratzer,

Risposte:


28

Un riferimento canonico per questo tipo di domanda è What Grap Different di Paul Graham . Le due restanti caratteristiche chiave di Lisp che non sono ampiamente disponibili, secondo questo articolo al momento della sua stesura, sono:

8. Una notazione per il codice che usa alberi di simboli.

9. L'intera lingua è sempre disponibile. Non esiste una vera distinzione tra tempo di lettura, tempo di compilazione e runtime. È possibile compilare o eseguire il codice durante la lettura, leggere o eseguire il codice durante la compilazione e leggere o compilare il codice in fase di esecuzione.

Il commento affronta ogni punto e nomina le lingue popolari in cui tale funzione è disponibile.

8, che (con 9) è ciò che rende possibili le macro di Lisp, è finora ancora unico per Lisp, forse perché (a) richiede quei genitori, o qualcosa di altrettanto grave, e (b) se aggiungi quell'incremento finale di potenza , non puoi più affermare di aver inventato una nuova lingua, ma solo di aver progettato un nuovo dialetto di Lisp; -)

Si noti che questo articolo è stato rivisto l'ultima volta nel 2002 e negli ultimi 11 anni ci sono state molte nuove lingue, alcune delle quali potrebbero incorporare tutte queste funzionalità Lisp nel loro design.


2
Queste funzionalità non sono esclusive di Lisp (e varianti derivate direttamente) e non lo sono da molto tempo.
Donal Fellows,

21
@iceX: la differenza tra JavaScript e lingue come Lisp, Smalltalk, Self, Newspeak, APL, Factor, Forth ecc. è che in JavaScript i programmi sono "morti". Sono file di testo. Si interrompe il programma in esecuzione, si modifica il file di testo, quindi si avvia una copia completamente nuova del programma. In quegli altri (cosiddetti ambienti "vivaci"), non si ferma mai il programma in esecuzione, lo stesso programma in esecuzione è un insieme di oggetti in memoria che si manipolano allo stesso modo in cui si manipola qualsiasi altro oggetto, mentre è in esecuzione . (Nota: questo non è vero per Clojure, ma la maggior parte dei vecchi Lisps lo fanno in questo modo.)
Jörg W Mittag

2
@ JörgWMittag Wow ... Quindi, i linguaggi di programmazione come Clojure, Clojurescript, lispyscript non sono in realtà "real lisp" perché non possono cambiare il loro codice come fa il Lisp della vecchia scuola, come fa Common Lisp. Ma ... se non riescono a cambiarlo ... perché preoccuparsi di usare l'espressione S che pensavo fosse allo scopo di manipolare il codice ... (sentendomi come se fossi appena caduto in una buca di coniglio scura e profonda)
iceX

4
@iceX: molte lingue hanno evalo simili, e molte possono fare metaprogrammazione, ad esempio Template Haskell di Haskell. La cosa (probabilmente) unica di Lisp è che la rappresentazione per dati, codice e meta-codice è la stessa - non solo nella sintassi, ma è davvero la stessa cosa.
tdammers,

2
@iceX Eval è solo una parte di esso, ma non tutto. In Lisp puoi eseguire il codice in fase di compilazione. È possibile eseguire il codice in fase di lettura. Sì, il clojure supporta tutto il dinamismo di Lisp, ha macro, macro dei lettori ed eval, oltre alla possibilità di collegare un REPL a un programma in esecuzione per modificarlo al volo.
Stonemetal,

15

Alla domanda è difficile rispondere, poiché qualcuno dovrebbe conoscere tutte le lingue per sapere che nessun altro ha una particolare funzione disponibile in Lisp, quindi quanto segue si basa sulle lingue con cui ho esperienza.

In cima alla mia testa, le condizioni sono qualcosa che non ho visto in nessun'altra lingua. Pensa alle "eccezioni", ma in cui lo stack di chiamate non viene svolto e in cui il chiamante può inviare un valore di recupero al sito delle eccezioni, ma senza disturbare lo stack di chiamate tra il gestore e l'origine dell'eccezione. Ad essere onesti, questa è davvero solo un'applicazione speciale di continuazioni, quindi Ruby e Scheme (almeno) possono farlo.

Il macrosistema di Lisp beneficia della regolarità / omoiconicità, ma Scala sta pianificando di incorporarli come funzionalità stabile in 2.12 e Template Haskell rivendica caratteristiche simili. Direi che saranno più sintatticamente complessi che con Lisp, ma la generazione del codice in fase di compilazione è lì a prescindere.

Vieni a pensarci, però, la semplice costruzione di moduli è solo un tipo di macro disponibile in Lisp: non ho mai visto un equivalente di macro di compilatore o lettore da nessun'altra parte.

La capacità di alcuni dialetti (ad es. SBCL ) di salvare un'immagine di processo completa e riprendibile è interessante, ma ancora una volta non è unica: Smalltalk lo fa da decenni.

Molte altre lingue consentono l'assegnazione destrutturante quando si restituiscono le matrici, ma i valori # 'e #' approccio multi-valore-bind / let-value sembrano ancora essere specifici di Common Lisp e Scheme (che può comunque fare anche una 'normale' destrutturazione ). Il "wantarray" di Perl consente a una funzione di determinare se viene chiamata in un contesto scalare, elenco o vuoto in modo da poter adattare il suo valore di ritorno in un modo simile (-ish), ma non ho visto valori di ritorno multipli "veri" all'esterno del regime / CL.

In termini di funzionalità linguistiche, probabilmente non c'è molto che Lisp possa fare, mentre altre lingue non possono (la completezza di Turing è quella che è). Ciò che è , tuttavia, è un linguaggio in cui il codice viene espresso in termini di proprie strutture di dati, rendendo la Big Idea ™ - quel codice sono dati - qualcosa con cui è relativamente facile lavorare.


3
Le macro Scala sono molto meno potenti delle macro Lisp, così come le macro TH, le macro igieniche Scheme, ecc. Un sistema altrettanto potente può essere trovato in MetaLua e Converge.
SK-logic

4

Dopo così tanti decenni, non penso che ci sia qualcosa di esclusivo in Lisp. Ma anche oggi ci sono molte cose interessanti che sono difficili da trovare fuori da Lisps. Alcune cose che mi vengono in mente:

  • Un sistema di oggetti di alta qualità con sofisticati meta-protocolli (ad esempio CLOS) non è molto diffuso.
  • di tanto in tanto compaiono diversi metodi, ma la maggior parte delle persone non ne ha mai sentito parlare.
  • Come altri hanno sottolineato, il sistema delle condizioni è piuttosto sofisticato rispetto ai popolari meccanismi di gestione delle eccezioni.
  • Una rappresentazione compatta della semantica del linguaggio (eval), un approccio che abilita a definire la propria lingua e rendendola disponibile per adattamenti profondi semplici (vedi SICP ) - le funzioni "eval" delle attuali lingue popolari semplicemente non condividono le stesse proprietà.

Infine, c'è molto altro da imparare da Lisp che non riguarda la lingua stessa ma è diventato parte della storia di Lisp e si è perso nel tempo. Ad esempio Interlisp, Symbolics Genera, ecc ... se non metti mai le mani su Genera, vedi questo thread comp.lang.lisp in cui Kent Pitman descrive come "Emacs è solo un'ombra pallida degli Zmac di Genera", che è stato tutto abilitato da avere un potente sistema Lisp di cui faceva parte Zmacs, che girava su una macchina Lisp.


Non ho mai visto una buona spiegazione dei multimetodi che li distinguessero dai metodi sovraccarichi , una caratteristica standard in quasi tutti i moderni linguaggi imperativi, tranne per il fatto che la spedizione multimetodi viene risolta dinamicamente in fase di esecuzione ed è quindi molto più lenta dell'uso dei metodi sovraccarichi, che vengono risolti al momento della compilazione.
Mason Wheeler,

Hanno importanti distinzioni. Se non riesci a trovare risorse a riguardo, puoi sempre creare una nuova domanda qui ...
Thiago Silva,

Julia ha multimetodi e il polimorfismo parametrico di Haskell è una specie di multimetodi. Esistono modi per simulare metodi multipli in molte lingue. Il sistema delle condizioni è qualcosa che desidero particolarmente (modifica e continua, anche se l'ho visto per i debugger C #).
aoeu256,

4

Non è necessariamente una singola caratteristica . È l'intero aspetto e il modo in cui determinati set di funzionalità funzionano insieme.

JavaScript o Java hanno molte funzionalità di Lisp (macchina virtuale, compilatore / valutatore, garbage collection, ecc.). Ma JavaScript ad esempio manca della parte di programmazione simbolica, manca di capacità matematiche (internamente ha solo float), manca la gestione degli errori e così via.

Molti sistemi Lisp comuni sono ottimizzati per un modo di sviluppo in cui si estende il nuovo software in modo incrementale, estendendo il linguaggio Lisp in varie dimensioni utilizzando varie tecniche di meta-programmazione - senza riavviare il software per molto tempo. Pertanto, deve essere flessibile ed estensibile, ma allo stesso tempo deve essere robusto. Cambiare la lingua (le macro sono sostanzialmente un modo per l'utente di estendere il compilatore) senza interrompere il programma.

Ora qualcosa come JavaScript viene utilizzato anche per estendere un programma, in genere un browser web. Ma la maggior parte delle volte non si fa molta meta-programmazione in JavaScript - oltre ad alcuni hacker OOP .

Esempio:

Si può implementare un software matematico avanzato generale per il dominio dell'algebra del computer principalmente in due modi: scrivere il motore in C con un linguaggio specializzato in alto (come Mathematica ) o in qualche dialetto Lisp più avanzato. Macsyma / Maxima in Common Lisp, Riduzione in Standard Lisp, Axiom in Common Lisp.

(Ce ne sono anche uno o più scritti in Python.)

Non ci sono molti sistemi che offrono il set di funzionalità di qualcosa come Axiom , che gira su Common Lisp.

Ciò che ha reso Lisp attraente per questi tipi di applicazioni è un mix di funzionalità: matematica di base avanzata (bignum, rapporti, ...), calcolo simbolico, compilatore interattivo, ecc. È del tutto possibile ottenere queste cose implementandole in un basso lingua di livello. In questo modo, uno avrà implementato il 50% o più di un tipico sistema Lisp.


2

Non per quanto ne sono a conoscenza. Forth è facilmente dinamico come Lisp, forse più perché il codice dinamico in Forth assomiglia al normale codice Forth mentre le macro Lisp tendono ad usare funzioni diverse rispetto al normale codice Lisp ( almeno in Clojure non ho mai usato la citazione di sintassi al di fuori di una macro) e di conseguenza sembrano molto diversi dal normale codice Lisp. Come esempio di quanto sia dinamico Forth, ecco un modo per implementare i commenti in Forth :

: (   41 word drop ; immediate
( That was the definition for the comment word. )
( Now we can add comments to what we are doing! )

1
Per quanto Forth sia divertente, l'ho sempre trovato irrimediabilmente opaco e imbarazzante, forse perché è basato sullo stack e tutto è invertito.
Robert Harvey,

2
Per curiosità, cosa intendi con "separato dalla parte principale della lingua"? Le macro in Lisp hanno pieno accesso a tutte le funzioni definite nel punto in cui è definita la macro, ognuna delle quali può essere utilizzata per creare il modulo in cui la macro si espande. Ciò potrebbe comportare informazioni recuperate da un database e / o server. Il tuo esempio di commento, potrebbe essere definito come: (commento defmacro (e corpo di riposo)), vero?
Danny Woods,

1
@DannyWoods Voglio dire, le macro non sembrano un codice normale. Almeno nel clojure puoi distinguere il codice macro dal codice normale perché usa pesantemente la citazione della sintassi, lo splicing non quotato, ecc. Che è insolito nel codice normale. Quel codice di esempio che ho fornito sembra un normale codice fino a quando non vedi l'immediato. Rileggendo la mia risposta, è espressa male.
Stonemetal,

1
@stonemetal Nessun problema, ma vale la pena notare che non c'è nulla di specifico per la macro sulla citazione della sintassi, in Common Lisp o Clojure. A volte è conveniente avere espressioni di backtick nelle definizioni delle funzioni regolari e i corpi macro possono essere creati manualmente con elenchi semplici (anche se devi solo farlo una o due volte per decidere che la citazione della sintassi è una buona cosa!)
Danny Woods

1
Per essere onesti, lo stesso può essere fatto in Lisp, registrando una macro di lettore personalizzata.
fjarri,

2

Lisp ha molti dialetti e ognuno di essi ha il proprio set di funzionalità. La mia caratteristica preferita che è improbabile che venga adottata da qualsiasi altra lingua è la "pila di spaghetti" di Interlisp .

La pila di spaghetti è come una chiusura, ma con steroidi. Salva non solo la funzione corrente, ma l'intero contesto fino alla cima dello stack. Qualcosa come una co-routine , tranne per il fatto che è possibile crearli arbitrariamente, risultando in una gerarchia di contesti di stack.


Non lo sapevo, lo verificherò, grazie per la risposta :)
IceX

7
È lo stesso delle continuazioni di Scheme?
Nicola Musatti,

1
@NicolaMusatti, uno "stack di spaghetti" è una strategia di implementazione comune per le continuazioni simili a Scheme (che può essere richiamata dopo il ritorno della funzione che le ha create).
Alex D
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.