Perché alcuni linguaggi di programmazione funzionale utilizzano uno spazio per l'applicazione delle funzioni?


34

Dopo aver esaminato alcuni linguaggi per la programmazione funzionale, mi sono sempre chiesto perché alcuni linguaggi fp utilizzino uno o più caratteri di spazi bianchi per l'applicazione (e la definizione) delle funzioni, mentre la maggior parte (tutti?) I linguaggi imperativi / orientati agli oggetti usano parentesi, il che sembra essere il modo più matematico. Penso anche che quest'ultimo stile sia molto più chiaro e leggibile che senza i genitori.

Quindi se abbiamo una funzione f (x) = x² ci sono le due alternative per chiamarla:

  • FP: f x

    Esempi:

    • ML, Ocaml, F #
    • Haskell
    • LISP, Schema (in qualche modo)
  • Non-FP: f(x)

    Esempi:

    • Quasi tutte le lingue imperative (lo so, vedi i commenti / le risposte)
    • Erlang
    • Scala (consente anche la "notazione operatore" per singoli argomenti)

Quali sono i motivi per "tralasciare" le parentesi?


9
Per renderlo più confuso, erm, intendevo succinto .
Den

11
@Den se impari l'hashell, la sintassi sarà una delle cose meno confuse: p
Simon Bergot

5
Ti rendi conto dell'ironia di dire che usare le parentesi sarebbe più matematico e quindi usare la funzione di esponenziazione come esempio?
Jörg W Mittag,

12
@Den ti stai facendo del male rifiutando una lingua a causa della sua sintassi. Sicuramente non hai avuto problemi ad imparare xml o sql (quelli non sono linguaggi generici, ma definiscono la propria sintassi).
Simon Bergot,

7
Vorrei sottolineare che gli script di shell (ad esempio bash) generalmente non usano parametri per chiamare i comandi. PowerShell ha il peggio di entrambi i mondi in quanto le funzioni sono dichiarate tra parentesi e chiamate senza di esse.
Kris Harper,

Risposte:


59

che sembra essere il modo più matematico

i linguaggi funzionali sono ispirati dal calcolo lambda . In questo campo, le parentesi non vengono utilizzate per l'applicazione delle funzioni.

Penso anche che quest'ultimo stile sia molto più chiaro e leggibile che senza i genitori.

La leggibilità è negli occhi di chi guarda. Non sei abituato a leggerlo. È un po 'come gli operatori matematici. Se capisci l'associatività, hai solo bisogno di alcune parentesi per chiarire la struttura della tua espressione. Spesso non ne hai bisogno.

Currying è anche un buon motivo per usare questa convenzione. In haskell, puoi definire quanto segue:

add :: Int -> Int -> Int
add x y = x + y

x = add 5 6 -- x == 11
f = add 5
y = f 6 -- y == 11
z = ((add 5) 6) -- explicit parentheses; z == 11

Con le parentesi puoi usare due convenzioni: f(5, 6)(non al curry) o f(5)(6)(al curry). La sintassi haskell aiuta ad abituarsi al concetto di curry. Puoi comunque utilizzare una versione non curry, ma è più doloroso usarla con i combinatori

add' :: (Int, Int) -> Int
add' (x, y) = x + y
u = add'(5, 6) -- just like other languages
l = [1, 2, 3]
l1 = map (add 5) l -- [6, 7, 8]
l2 = map (\x -> add'(5, x)) l -- like other languages

Notare come la seconda versione ti costringe a registrare x come variabile e che la sottoespressione è una funzione che accetta un numero intero e ne aggiunge 5? La versione al curry è molto più leggera, ma è anche considerata da molti più leggibile.

I programmi di Haskell fanno ampio uso di applicazioni parziali e combinatori come mezzo per definire e comporre astrazioni, quindi questo non è un esempio giocattolo. Una buona interfaccia funzionale sarà quella in cui l'ordine dei parametri fornisce un uso intuitivo al curry.

Un altro punto: una funzione senza parametri dovrebbe essere chiamata con f(). In haskell, poiché manipoli solo valori valutati pigri immutabili, lo scrivi fe lo consideri come un valore che dovrà eseguire alcuni calcoli quando necessario. Poiché la sua valutazione non avrà alcun effetto collaterale, non ha senso avere una notazione diversa per la funzione senza parametri e il suo valore restituito.

Esistono anche altre convenzioni per l'applicazione delle funzioni:

  • Lisp: (fx) - prefisso con parentesi esterne
  • Avanti: xf - postfix

7
Minor nitpick: i termini sono "curry" e "curry", non "currificazione" e "currificato".
Doval,

"non curry" cos'è una funzione non curry in haskell?
Ven

3
@ user1737909 Una funzione che accetta una tupla come argomento.
Doval,

1
@Doval In realtà dovrebbe essere “schönfinkeling” e “schönfinkeled”. Ma vabbè, la storia definisce sempre il vincitore in modo retrospettivo.
Profpatsch,

Pensando a una sintassi del curriculum tra parentesi, qualcosa come f(a, ,b)o add(a, )o add(, b)sembrerebbe più flessibile di default.
galleria

25

L'idea di base è rendere l'operazione più importante (applicazione della funzione) più facile da leggere e più facile da scrivere. Uno spazio è molto poco invadente da leggere e molto facile da scrivere.

Nota che questo non è specifico per i linguaggi funzionali, ad esempio in Smalltalk , una delle prime lingue OO e lingue ispirate da esso ( Self , Newspeak , Objective-C), ma anche in Io e nelle lingue ispirate da esso (Ioke, Seph), lo spazio bianco viene utilizzato per le chiamate di metodo.

Smalltalk-style:

anArray sort
anArray add: 2
aString replace: "a" with: "b"

(In quest'ultimo caso, il nome del metodo è replace:with:.)

Io-style:

anArray sort
anArray add(2)
aString replace("a", "b")

Scala consente inoltre spazi bianchi per le chiamate di metodo:

foo bar(baz)

E tralasciando le parentesi se c'è un solo argomento:

foo bar baz

Ruby ti consente anche di lasciare le parentesi:

an_array.sort
an_array.add 2
a_string.replace "a", "b"

usando le parentesi, che sembra essere il modo più matematico

Non proprio:

f(x)
sin x
x²
|x|
x!
x + y
xy
½

La notazione matematica si è evoluta a lungo in un modo orribilmente incoerente.

Se non altro, i linguaggi funzionali si ispirano al calcolo λ dove l'applicazione della funzione è scritta usando gli spazi bianchi.


3
C'è anche un argomento per l'economia dell'editing. In particolare, i linguaggi funzionali in genere supportano direttamente la composizione. Mettere le parentesi attorno alla funzione anziché all'argomento ha senso. Devi farlo "una volta": (e. F. G) x rispetto a e (f (g (x))). Inoltre, Haskell, almeno, non impedirebbe a uno di fare f (x) invece di f x. Non è solo idiomatico.
nomen,

18

Le parentesi per l'applicazione delle funzioni sono solo una delle tante selle che Euler ci ha lasciato. Come ogni altra cosa, la matematica ha bisogno di convenzioni quando ci sono diversi modi per fare qualcosa. Se la tua educazione matematica si estende solo fino a una materia non matematica all'università, allora probabilmente non hai troppa familiarità con i molti campi in cui l'applicazione delle funzioni avviene felicemente senza nessuna di queste parentesi senza senso (ad esempio Geometria differenziale, Algebra astratta). E non menzionate nemmeno le funzioni che vengono applicate infix (quasi tutte le lingue), "outfix" come prendere una norma o diagrammaticamente (Befunge, topologia algebrica).

Quindi, in risposta, direi che è dovuto a una percentuale molto più alta di programmatori funzionali e progettisti di lingue che hanno una vasta educazione matematica. Von Neumann dice "Giovane, in matematica non capisci le cose. Ti ci abitui e basta.", Questo è certamente vero per la notazione.


7
Non farmi nemmeno iniziare contro f(x)vs. sin xvs. vs. |x|vs. x!vs. x + yvs. xyvs. ½vs. una somma contro un integrale vs. ...
Jörg W Mittag

2
+1 per la citazione di von Neuman. +1 per il resto della risposta, ma non posso dare +2. :(
pvorb,

2
Prendo un pizzico di elitarismo qui; Contesto la neutralità di "i progettisti di linguaggi di programmazione funzionale sono meglio istruiti" .
CaptainCodeman,

7
@CaptainCodeman Dice più istruito in matematica, una dichiarazione con cui sono d'accordo. Almeno quando si tratta di Haskell, puoi notare che entrambi i concetti sono di natura più matematica e che anche la comunità è più incline matematicamente. Non sono più intelligenti, solo meglio istruiti in matematica.
Paul,

5
@CaptainCodeman No, dal momento che non ha mai sottinteso che sono più istruiti in generale, solo più istruiti in matematica. Questo è esattamente ciò che ha detto: "vasta educazione matematica". :)
Paul,

8

Sebbene ci sia molta verità nella risposta di Simon, penso che ci sia anche una ragione molto più pratica. La natura della programmazione funzionale tende a generare molte più parentesi rispetto alla programmazione imperativa, a causa del concatenamento e della composizione delle funzioni. Questi schemi di concatenazione e composizione di solito capita anche di essere inequivocabilmente rappresentati senza parentesi.

La linea di fondo è che tutte quelle parentesi diventano fastidiose da leggere e tracciare. È probabilmente il motivo numero uno per cui LISP non è più popolare. Quindi, se riesci a ottenere il potere della programmazione funzionale senza il fastidio delle parentesi in eccesso, penso che i progettisti linguistici tenderanno ad andare in quel modo. Dopotutto, i progettisti linguistici sono anche utenti linguistici.

Anche Scala consente al programmatore di omettere le parentesi in determinate circostanze, che si presentano abbastanza frequentemente quando si programma in uno stile funzionale, in modo da ottenere il meglio da entrambi i mondi. Per esempio:

val message = line split "," map (_.toByte)

Le parentesi alla fine sono necessarie per l'associatività e le altre sono lasciate fuori (così come i punti). Scriverlo in questo modo enfatizza la natura incatenata delle operazioni che stai eseguendo. Potrebbe non essere familiare a un programmatore imperativo, ma a un programmatore funzionale è molto naturale e scorrevole scrivere in questo modo, senza dover interrompere e inserire la sintassi che non aggiunge alcun significato al programma, ma è lì solo per rendere felice il compilatore .


2
"La linea di fondo è che tutte quelle parentesi diventano fastidiose da leggere e tracciare. Probabilmente è la ragione numero uno per cui LISP non è più popolare.": Non penso che le parentesi siano una ragione per cui Lisp non è popolare: non penso sono particolarmente difficili da leggere e trovo che altre lingue abbiano una sintassi molto più imbarazzante.
Giorgio,

2
La sintassi LISP compensa in gran parte le fastidiose parentesi con il suo altissimo grado di ortogonalità. Ciò significa solo che ha altre funzionalità di riscatto che lo rendono ancora utilizzabile, non che i genitori non siano fastidiosi. Capisco che non tutti la pensano così, ma la stragrande maggioranza dei programmatori che ho incontrato lo fanno.
Karl Bielefeldt,

"Lost In Stupid Parentheses" è il mio backronym LISP preferito. I computer possono anche utilizzare il modo umano di specificare blocchi di codice e eliminare la ridondanza, come in lingue come Wisp .
Cees Timmerman,

4

Entrambe le premesse sono sbagliate.

  • Questi linguaggi funzionali non usano spazio per l'applicazione delle funzioni. Quello che fanno è semplicemente analizzare qualsiasi espressione che viene dopo una funzione come argomento.

    GHCi> f 3
    4
    GHCi> f (3)
    4
    GHCi> (f) 3
    4

    Ovviamente se non usi uno spazio né parentesi, normalmente non funzionerà, semplicemente perché l'espressione non è correttamente tokenizzata

    GHCi> f3

    <‌interattivo>: 7: 1:
        Non nell'ambito: 'f3'
        Forse intendevi 'f' (linea 2)

    Ma se queste lingue fossero limitate ai nomi delle variabili di 1 carattere, funzionerebbe anche.

  • Né le convenzioni matematiche normalmente richiedono parentesi per l'applicazione delle funzioni. Non solo i matematici spesso scrivono sin x - per funzioni lineari (di solito chiamate operatori lineari allora) è anche molto usuale scrivere l'argomento senza parentesi, forse perché (proprio come qualsiasi funzione nella programmazione funzionale) le funzioni lineari sono spesso gestite senza fornire direttamente un argomento.

Quindi, davvero, la tua domanda si riduce a: perché alcune lingue richiedono parentesi per l'applicazione delle funzioni? Beh, a quanto pare alcune persone, come te, lo considerano più leggibile. Non sono assolutamente d'accordo: una coppia di parentesi è carina, ma non appena ne hai nidificati più di due, inizia a diventare confuso. Il codice Lisp lo dimostra meglio e praticamente tutti i linguaggi funzionali apparirebbero in modo simile se avessi bisogno di parentesi ovunque. Ciò non accade molto nelle lingue imperative, ma principalmente perché queste lingue non sono abbastanza espressive per scrivere in primo luogo linee chiare e concise.


Ho capito, la domanda non è perfetta. :) Quindi la conclusione da trarre è: "Le parentesi non si adattano".
pvorb,

2
Né sono linguaggi non funzionali che unanimi nel richiedere parentesi; per alcuni esempi, Smalltalk ha object method: args, Objective C ha [object method: args]e Ruby e Perl consentono entrambi di omettere parentesi nelle chiamate di funzioni e metodi, richiedendo solo loro di risolvere l'ambiguità.
Hobbs,

1

Un piccolo chiarimento storico: la notazione originale in matematica era senza parentesi !

La notazione fx per una funzione arbitraria di x fu introdotta da Johan Bernoulli intorno al 1700 poco dopo che Leibniz iniziò a parlare di funzioni (in realtà Bernoulli scrisse ϕ x ). Ma prima di allora molte persone utilizzavano già notazioni come sin x o lx (il logaritmo di x ) per funzioni specifiche di x , senza parentesi. Sospetto che sia la ragione per cui molte persone oggi scrivono ancora sin x invece di sin (x) .

Eulero, che era uno studente di Bernoulli, adottò la ϕ x di Bernoulli e cambiò il greco in una lettera latina, scrivendo fx . In seguito ha notato che potrebbe essere facile confondere f per una "quantità" e credere che fx fosse un prodotto, quindi ha iniziato a scrivere f: x . Quindi la notazione f (x) non è dovuta a Eulero. Ovviamente Eulero aveva bisogno di parentesi per lo stesso motivo per cui abbiamo ancora bisogno di parentesi nei linguaggi di programmazione funzionale: distinguere per esempio (fx) + y da f (x + y) . Sospetto che le persone dopo Euler abbiano adottato la parentesi come impostazione predefinita perché hanno visto principalmente Euler scrivere espressioni composte come f (ax + b) . Di rado scriveva solo fx .

Per di più su questo vedi: Euler ha mai scritto f (x) tra parentesi? .

Non so se i progettisti di linguaggi di programmazione funzionale o gli inventori del calcolo lambda fossero a conoscenza delle radici storiche della loro notazione. Personalmente trovo più naturale la notazione senza parentesi.

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.