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

33

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

Quindi se abbiamo una funzione f (x) = x² ci sono 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 "notazione dell'operatore" per singoli argomenti)

Quali sono i motivi per "tralasciare" le parentesi?

    
posta pvorb 08.07.2014 - 09:42
fonte

6 risposte

58

which seems to be the more mathematical way

le lingue funzionali sono ispirate al calcolo lambda . In questo campo, le parentesi non vengono utilizzate per l'applicazione della funzione.

I also think that the latter style is much more clear and readable than without the parens.

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

Il currying è anche una buona ragione 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 i parents, potresti usare due convenzioni: f(5, 6) (non al curry) o f(5)(6) (al curry). La sintassi haskell aiuta ad abituarsi al concetto di curriculum. Puoi ancora usare una versione non curata, 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

Si noti come la seconda versione impone di registrare x come variabile e che la sottoespressione è una funzione che prende un intero e ne aggiunge 5? La versione al curry è molto più leggera, ma anche considerata da molti più leggibile.

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

Un altro punto: una funzione senza parametri dovrebbe essere chiamata con f() . In haskell, dal momento che si manipolano solo valori valutati pigri immutabili, basta scrivere f e considerarlo 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 di funzioni:

  • Lisp: (f x) - prefisso con parentesi esterna
  • Forth: x f - postfix
risposta data 08.07.2014 - 10:45
fonte
24

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

Si noti che questo non è specifico per le lingue funzionali, ad es. in Smalltalk , una delle prime lingue e lingue OO ad ispirarsi ( Self , Newspeak , Objective-C), ma anche in Io e nelle lingue da esso ispirate (Ioke, Seph), lo spazio bianco è usato per 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 anche spazi bianchi per le chiamate ai metodi:

foo bar(baz)

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

foo bar baz

Ruby ti permette anche di lasciare le parentesi:

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

using parentheses, which seems to be the more mathematical way

Non proprio:

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

La notazione matematica si è evoluta per lungo tempo in modo orribilmente incoerente.

Se del tutto, le lingue funzionali traggono ispirazione dal calcolo λ in cui l'applicazione della funzione viene scritta utilizzando spazi bianchi.

    
risposta data 08.07.2014 - 17:36
fonte
17

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

Quindi in risposta, direi che è dovuto a una proporzione molto più alta di programmatori funzionali e progettisti di linguaggi che hanno una vasta formazione matematica. Von Neumann dice "Giovanotto, in matematica non capisci le cose, ti abitui solo a loro.", Certamente questo è vero per la notazione.

    
risposta data 08.07.2014 - 13:01
fonte
7

Anche se c'è molta verità nella risposta di Simon, penso che ci sia anche un motivo molto più pratico. La natura della programmazione funzionale tende a generare molte più parentesi rispetto alla programmazione imperativa, a causa del concatenamento e della composizione delle funzioni. Anche questi schemi di concatenamento e composizione sono solitamente in grado di essere rappresentati senza ambiguità senza parentesi.

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. Quindi, se riesci a ottenere il potere della programmazione funzionale senza il fastidio di parentesi in eccesso, penso che i progettisti di linguaggio tenderanno ad andare in quel modo. Dopo tutto, i progettisti di linguaggi sono anche utenti di lingue.

Anche Scala consente al programmatore di omettere le parentesi in determinate circostanze, che si verificano abbastanza frequentemente durante la programmazione in uno stile funzionale, in modo da ottenere il meglio da entrambi i mondi. Ad esempio:

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

I paren alla fine sono necessari per l'associatività, e gli altri sono lasciati fuori (così come i punti). Scrivendolo in questo modo si enfatizza la natura concatenata delle operazioni che si stanno eseguendo. Potrebbe non essere familiare a un programmatore imperativo, ma a un programmatore funzionale sembra molto naturale e fluente scrivere in questo modo, senza dover interrompere e inserire la sintassi che non aggiunge significato al programma, ma è proprio lì per rendere felice il compilatore .

    
risposta data 08.07.2014 - 18:06
fonte
4

Entrambi i locali sono sbagliati.

  • Questi linguaggi funzionali non usano lo spazio per l'applicazione di 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

    Naturalmente se usi uno spazio né parents allora normalmente non funzionerà, semplicemente perché l'espressione non è correttamente tokenizzata

    GHCi> f3

    <‌interactive>:7:1:
        Not in scope: ‘f3’
        Perhaps you meant ‘f’ (line 2)

    Ma se queste lingue fossero limitate a nomi di variabili a 1 carattere, allora funzionerebbe anche.

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

Quindi in realtà la tua domanda si riduce a: perché alcune lingue richiedono parentesi per l'applicazione di funzioni? Bene, a quanto pare alcune persone, come te, considerano questo più leggibile. Non sono assolutamente d'accordo: una coppia di parents è carina, ma non appena ne hai più di due, inizia a confondersi. Il codice Lisp lo dimostra al meglio, e praticamente tutti i linguaggi funzionali apparirebbero ugualmente ingombranti se avessi bisogno di paren ovunque. Questo non accade tanto nelle lingue imperative, ma principalmente perché queste lingue non sono abbastanza espressive per scrivere in primo luogo concise e chiare one-liner.

    
risposta data 09.07.2014 - 01:30
fonte
0

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 verso il 1700 poco dopo che Leibniz iniziò a parlare di funzioni (in realtà Bernoulli scrisse φ x ). Ma prima c'erano molte persone che già usavano notazioni come sin x o lx (il logaritmo di x ) per specifico funzioni di x , senza parentesi. Sospetto che sia la ragione per cui molte persone scrivono ancora sin x invece di sin (x) .

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

Avanti su questo punto: Euler ha mai scritto f (x ) con parentesi? .

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

    
risposta data 28.07.2018 - 18:30
fonte

Leggi altre domande sui tag