Haskell, Lisp e verbosity [chiuso]


104

Per quelli di voi esperti sia in Haskell che in qualche sapore di Lisp, sono curioso di quanto sia "piacevole" (per usare un termine orribile) scrivere codice in Haskell contro Lisp.

Qualche background: sto imparando Haskell ora, avendo precedentemente lavorato con Scheme e CL (e una piccola incursione in Clojure). Tradizionalmente, potresti considerarmi un fan dei linguaggi dinamici per la brevità e la rapidità che forniscono. Mi sono subito innamorato delle macro Lisp, poiché mi hanno dato un altro modo per evitare verbosità e boilerplate.

Trovo Haskell incredibilmente interessante, poiché mi sta introducendo a metodi di codifica che non sapevo esistessero. Ha sicuramente alcuni aspetti che sembrano aiutare a raggiungere l'agilità, come la facilità di scrivere funzioni parziali. Tuttavia, sono un po 'preoccupato per la perdita delle macro Lisp (presumo di perderle; a dire il vero, potrei semplicemente non averne ancora imparato?) E il sistema di digitazione statica.

Qualcuno che ha fatto una discreta quantità di programmazione nella mente di entrambi i mondi commenterebbe come le esperienze differiscono, cosa preferisci e se detta preferenza è situazionale?

Risposte:


69

Risposta breve:

  • quasi tutto ciò che puoi fare con le macro puoi fare con una funzione di ordine superiore (e includo monadi, frecce, ecc.), Ma potrebbe richiedere più pensiero (ma solo la prima volta, ed è divertente e sarai un programmatore migliore per questo) e
  • il sistema statico è sufficientemente generale da non intralciarti mai, e sorprendentemente in realtà "aiuta a raggiungere l'agilità" (come hai detto) perché quando il tuo programma compila puoi essere quasi certo che sia corretto, quindi questa certezza ti permette di provare fuori cose che altrimenti potresti avere paura di provare - c'è una sensazione "dinamica" nella programmazione sebbene non sia la stessa del Lisp.

[Nota: esiste un " Template Haskell " che ti consente di scrivere macro proprio come in Lisp, ma in senso stretto non dovresti mai averne bisogno .]


15
Da Conor McBride, citato da Don Stewart: "Mi piace pensare che i tipi distorcano la nostra gravità, in modo che la direzione di cui abbiamo bisogno per viaggiare [per scrivere programmi corretti] diventi" in discesa "." Il sistema dei tipi rende sorprendentemente facile scrivere programmi corretti ... guarda questo post e le sue ricondivisioni.
ShreevatsaR

3
Le funzioni di ordine elevato non possono sostituire le macro e, in effetti, CL ha entrambe per qualche motivo. Il vero potere delle macro in CL è che consentono allo sviluppatore di introdurre nuove funzionalità del linguaggio che aiutano a esprimere meglio la soluzione a un problema, senza dover attendere una nuova versione del linguaggio come in Haskell o Java. Ad esempio, se Haskell avesse questo potere non ci sarebbe bisogno che gli autori Haskell scrivano estensioni GHC, poiché potrebbero essere implementate dagli sviluppatori stessi come macro in qualsiasi momento.
mljrg

@mljrg Hai un esempio concreto? Vedi i commenti sulla risposta di Hibou57 di seguito in cui un presunto esempio si è rivelato dubbio. Sarei interessato a sapere che tipo di cose intendi (ad es. Codice Haskell con e senza macro).
ShreevatsaR

4
Porta fuori il curry da Haskell. Potresti implementarlo con ciò che rimarrebbe a Haskell? Un altro esempio: supponiamo che Haskell non supporti il ​​pattern matching, potresti aggiungerlo tu stesso senza che gli sviluppatori di GHC lo supportino? In CL, puoi usare le macro per estendere la lingua a tuo piacimento. Suppongo che sia per questo che il linguaggio CL non è cambiato dal suo standard negli anni '90, mentre Haskell sembra avere un flusso infinito di estensioni in GHC.
mljrg

64

Prima di tutto, non preoccuparti di perdere funzionalità particolari come la digitazione dinamica. Dato che hai familiarità con Common Lisp, un linguaggio straordinariamente ben progettato, presumo tu sia consapevole che un linguaggio non può essere ridotto al suo set di funzionalità. Si tratta di un insieme coerente, non è vero?

A questo proposito, Haskell brilla tanto quanto Common Lisp. Le sue caratteristiche si combinano per fornire un modo di programmazione che rende il codice estremamente breve ed elegante. La mancanza di macro è attenuata in qualche modo da concetti più elaborati (ma, allo stesso modo, più difficili da capire e da usare) come monadi e frecce. Il sistema di tipi statici aumenta la tua potenza piuttosto che intralciarti come nella maggior parte dei linguaggi orientati agli oggetti.

D'altra parte, la programmazione in Haskell è molto meno interattiva del Lisp e l'enorme quantità di riflessione presente in linguaggi come il Lisp non si adatta alla visione statica del mondo che Haskell presuppone. I set di strumenti a tua disposizione sono quindi abbastanza diversi tra le due lingue, ma difficili da confrontare tra loro.

Personalmente preferisco il modo di programmare Lisp in generale, poiché ritengo che si adatti meglio al modo in cui lavoro. Tuttavia, questo non significa che sei obbligato a farlo anche tu.


4
Potresti approfondire un po 'di più sulla "programmazione in Haskell è molto meno interattiva". GHCi non fornisce davvero tutto ciò di cui hai bisogno?
Johannes Gerer

3
@JohannesGerer: non l'ho provato, ma per quanto ho letto, GHCi non è una shell nell'immagine in esecuzione, in cui è possibile ridefinire ed estendere parti arbitrarie dell'intero programma mentre è in esecuzione. Inoltre, la sintassi Haskell rende molto più difficile copiare frammenti di programma tra il repl e l'editor a livello di programmazione.
Svante

13

C'è meno bisogno di metaprogrammazione in Haskell che in Common Lisp perché molto può essere strutturato attorno a monadi e la sintassi aggiunta rende i DSL incorporati meno simili ad alberi, ma c'è sempre Template Haskell, come menzionato da ShreevatsaR , e persino Liskell (semantica Haskell + Lisp sintassi) se ti piacciono le parentesi.


1
il collegamento Liskell è morto, ma di questi tempi c'è Hackett .
Will Ness

10

Per quanto riguarda le macro, ecco una pagina che ne parla: Hello Haskell, Goodbye Lisp . Spiega un punto di vista in cui le macro non sono necessarie in Haskell. Viene fornito con un breve esempio per il confronto.

Caso di esempio in cui è richiesta una macro LISP per evitare la valutazione di entrambi gli argomenti:

(defmacro doif (x y) `(if ,x ,y))

Caso di esempio in cui Haskell non valuta sistematicamente entrambi gli argomenti, senza la necessità di qualcosa come una definizione di macro:

doif x y = if x then (Just y) else Nothing

E voilà


17
È un'idea sbagliata comune. Sì, in Haskell la pigrizia significa che non hai bisogno di macro quando vuoi evitare di valutare alcune parti di un'espressione, ma quelle sono solo il sottoinsieme più banale di tutti gli usi delle macro. Google per "The Swine Before Perl" per un discorso che dimostra una macro che non può essere fatta con pigrizia. Inoltre, se si fa vuole un po 'per essere rigorosi, allora non si può fare che in funzione - che rispecchia il fatto che la Schema di delaynon può essere una funzione.
Eli Barzilay

8
@Eli Barzilay: non trovo questo esempio molto convincente. Ecco una traduzione Haskell completa e semplice della diapositiva 40: pastebin.com/8rFYwTrE
Reid Barton,

5
@Eli Barzilay: Non capisco affatto la tua risposta. accept è il (E) DSL. La acceptfunzione è l'analoga della macro delineata nelle pagine precedenti, e la definizione di vè esattamente parallela alla definizione di vScheme nella diapositiva 40. Le funzioni Haskell e Scheme calcolano la stessa cosa con la stessa strategia di valutazione. Nella migliore delle ipotesi, la macro consente di esporre una parte maggiore della struttura del programma all'ottimizzatore. Difficilmente si può affermare questo come un esempio in cui le macro aumentano la potenza espressiva del linguaggio in un modo non replicato da una valutazione pigra.
Reid Barton

5
@Eli Barzilay: In un ipotetico schema pigro, potresti scrivere questo: pastebin.com/TN3F8VVE La mia affermazione generale è che questa macro ti fa guadagnare pochissimo: sintassi leggermente diversa e un tempo più facile per l'ottimizzatore (ma non importa un "compilatore sufficientemente intelligente"). In cambio, ti sei intrappolato in un linguaggio inespressivo; come si definisce un automa che corrisponde a qualsiasi lettera senza elencarle tutte? Inoltre, non so cosa intendi per "usarlo in tutti i sottoelenchi" o "l'uso richiesto di dove con il proprio ambito".
Reid Barton

15
Ok, ci rinuncio. Apparentemente la tua definizione di DSL è "gli argomenti di una macro" e quindi il mio pigro esempio di Scheme non è un DSL, nonostante sia sintatticamente isomorfo all'originale ( automatondiventare letrec, :diventare accept, ->diventare nulla in questa versione). Qualunque cosa.
Reid Barton

9

Sono un programmatore Common Lisp.

Avendo provato Haskell qualche tempo fa, la mia linea di fondo personale era restare con CL.

Motivi:

  • digitazione dinamica (controlla la digitazione dinamica e statica: un'analisi basata su modelli di Pascal Costanza )
  • argomenti facoltativi e parole chiave
  • sintassi di lista omoiconica uniforme con macro
  • sintassi del prefisso (non è necessario ricordare le regole di precedenza)
  • impuro e quindi più adatto per la prototipazione rapida
  • potente sistema di oggetti con protocollo meta-oggetto
  • standard maturo
  • vasta gamma di compilatori

Haskell ha i suoi meriti ovviamente e fa alcune cose in un modo fondamentalmente diverso, ma per me semplicemente non lo taglia a lungo termine.


Ehi, hai il titolo di quel giornale di Costanza a cui ti sei collegato? Sembra che il file sia stato spostato.
michiakig

2
Nota che anche haskell supporta la sintassi del prefisso ma direi che monad >> = sarebbe molto molto brutto usarlo. Inoltre non sono d'accordo con l'impurità come una benedizione: P
alternativa

2
Mi piace questa nota a margine: non abbiamo ancora raccolto dati empirici se questo problema causi seri problemi nei programmi del mondo reale.
Jerome Baum

22
Nessuno degli esempi in quel documento (Pascal Costanza, Dynamic vs. Static Typing - A Pattern-Based Analysis ) si applica a Haskell. Sono tutti specifici di Java (o più precisamente, specifici della "programmazione orientata agli oggetti" ) e non riesco a vedere nessuno di questi problemi in arrivo in Haskell. Allo stesso modo, tutti gli altri tuoi argomenti sono discutibili: si può anche dire che Haskell è "puro e quindi più adatto per la prototipazione rapida", che la sintassi del prefisso non è obbligatoria, che non ha una vasta gamma di compilatori che fanno cose diverse , ecc.
ShreevatsaR

11
Quel documento è infatti quasi completamente irrilevante per Haskell. " dilbert = dogbert.hire(dilbert);" ?? Dubito che molti programmatori Haskell possano persino leggerlo senza contrarsi un po '.
sinistra intorno al

6

In Haskell puoi definire una funzione if, cosa impossibile in LISP. Ciò è possibile a causa della pigrizia, che consente una maggiore modularità nei programmi. Questo articolo classico: Perché FP è importante di John Hughes, spiega come la pigrizia migliora la componibilità.


5
Scheme (uno dei due principali dialetti LISP) in realtà ha una valutazione pigra, sebbene non sia predefinita come in Haskell.
Randy Voet,

7
(defmacro doif (xy) `(if, x, y))
Joshua Cheek

4
Una macro non è la stessa di una funzione: le macro non funzionano bene con funzioni di ordine superiore come fold, ad esempio, le funzioni non rigorose .
Tikhon Jelvis

5

Ci sono cose davvero interessanti che puoi ottenere in Lisp con macro che sono ingombranti (se possibile) in Haskell. Prendiamo ad esempio la macro "memoize" (vedi il capitolo 9 del PAIP di Peter Norvig). Con esso, puoi definire una funzione, ad esempio foo, e quindi semplicemente valutare (memoize 'foo), che sostituisce la definizione globale di foo con una versione memoizzata. Puoi ottenere lo stesso effetto in Haskell con funzioni di ordine superiore?


3
Non proprio (AFAIK), ma puoi fare qualcosa di simile modificando la funzione (supponendo che sia ricorsiva) per prendere la funzione da chiamare ricorsivamente come parametro (!) Piuttosto che chiamarsi semplicemente per nome: haskell.org/haskellwiki/Memoization
j_random_hacker

6
Puoi aggiungere foo a una struttura dati pigra, dove il valore verrà memorizzato una volta calcolato. Questo sarà effettivamente lo stesso.
Theo Belaire

Tutto in Haskell è memoizzato e probabilmente inline quando necessario per impostazione predefinita dal compilatore Haskell.
aoeu256

4

Mentre proseguo il mio viaggio di apprendimento Haskell, sembra che una cosa che aiuta a "sostituire" le macro sia la capacità di definire i propri operatori infissi e personalizzare la loro precedenza e associatività. Un po 'complicato, ma un sistema interessante!

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.