Composizione della funzione Haskell (.) E idiomi dell'applicazione della funzione ($): uso corretto


129

Ho letto Real World Haskell e mi sto avvicinando alla fine, ma una questione di stile mi sta prendendo in giro a che fare con gli operatori (.)e ($).

Quando scrivi una funzione che è una composizione di altre funzioni, la scrivi come:

f = g . h

Ma quando applichi qualcosa alla fine di quelle funzioni, lo scrivo in questo modo:

k = a $ b $ c $ value

Ma il libro lo scriverebbe così:

k = a . b . c $ value

Ora, per me sembrano funzionalmente equivalenti, fanno esattamente la stessa cosa ai miei occhi. Tuttavia, più guardo, più vedo le persone scrivere le loro funzioni come fa il libro: componi (.)prima e poi solo alla fine usi ($)per aggiungere un valore per valutare il lotto (nessuno lo fa con molte composizioni in dollari) .

C'è un motivo per usare il modo dei libri che è molto meglio dell'uso di tutti i ($)simboli? O c'è qualche buona pratica qui che non sto ottenendo? O è superfluo e non dovrei preoccuparmene affatto?


4
Si noti che il secondo esempio può essere fatto comek = a $ b $ c value
Thomas Eding,

1
Sì, come Zifre ha menzionato di seguito, ma ho deciso di lasciarlo lì perché non danneggia nulla e ha senso i suoi (e ora i tuoi) commenti. Grazie ugualmente. +1 :)
Robert Massaioli,

2
Un altro approccio comune è a . b $ c value, che non mi piace tanto quanto il terzo esempio, ma salva alcuni personaggi.
John L,

Risposte:


153

Immagino di poter rispondere all'autorità.

C'è un motivo per usare il modo dei libri che è molto meglio che usare tutti i simboli ($)?

Non c'è un motivo speciale. Sia io che Bryan preferiamo ridurre il rumore di linea. .è più silenzioso di $. Di conseguenza, il libro utilizza la f . g . h $ xsintassi.


3
Bene, non posso sperare in una risposta migliore di questa; da uno stesso autore. :) E questo ha senso, sembra molto più silenzioso sulla pagina mentre leggo. Contrassegnando questo come risposta perché risponde direttamente alla domanda. Grazie per la risposta; e, infatti, il libro.
Robert Massaioli,

38
Non essere in disaccordo con l'autore, ma penso che ci sia un'altra ragione più importante nel modello mentale di creare qualcosa piuttosto che usarlo . Gli utenti di Haskell tendono f.g.hpiuttosto a pensare a una nuova creazione intelligente f(g(h())). Ora chiamano una nuova funzione, anche se anonima, che hanno creato piuttosto che concatenare un grande dizionario di chiamate di funzioni preconfigurate come un utente PHP.
Evan Carroll,

1
È un'affermazione interessante: "riduci il rumore di linea" e "più silenzioso di". Non ho mai pensato ai linguaggi di programmazione in questa terminologia, ma ha perfettamente senso.
Rabarberski,

53

Sono davvero equivalenti: tieni presente che l' $operatore non fa praticamente nulla. f $ xvaluta f x. Lo scopo di $è il suo comportamento di fissità: giusta associazione e precedenza minima. Rimuovendo $e usando le parentesi per il raggruppamento anziché la precedenza infissa, i frammenti di codice sono simili al seguente:

k = a (b (c (value)))

e

k = (a . b . c) value

Il motivo per preferire la .versione rispetto alla $versione è lo stesso motivo per preferire entrambi rispetto alla versione molto tra parentesi sopra: fascino estetico.

Sebbene, alcuni potrebbero chiedersi se l'uso degli operatori infix invece delle parentesi si basa su un impulso inconscio per evitare ogni possibile somiglianza con Lisp (sto solo scherzando ... penso?).


38

Aggiungo che in f . g $ x, f . gè un'unità sintattica significativa.

Nel frattempo, in f $ g $ x, f $ gnon è un'unità significativa. Una catena di $è probabilmente più imperativa: prima ottieni il risultato gdi x, poi fai fad essa, poi fai fooad essa, quindi ecc.

Nel frattempo una catena di .è probabilmente più dichiarativa, e in un certo senso più vicina a una visione incentrata sul flusso di dati: comporre una serie di funzioni e infine applicarle a qualcosa.


2
Sto imparando Haskell (non ho codificato nulla di significativo) ma mi piace pensare a $ come una sorta di operatore "pipe". È molto simile al terminale | pipe (tranne che i dati scorrono da destra a sinistra), ma come i processi terminali ogni unità è esplicitamente indipendente.
LostSalad,

1
L' $operatore sembra comportarsi in modo simile <|all'operatore in F #. Credo che entrambi siano definiti uguali, in effetti - in F # (<|) a b = a be in Haskell a $ b = a b, che sono essenzialmente equivalenti.
Tom Galvin,

@Quackmatic Abbastanza sicuro che sia (<|) b a = a bin F #, poiché è usato come [1; 2; 3; 4; 5] <| List.map ((+) 1), se la memoria serve.
BalinKingOfMoria Ripristina CM

@BalinKingOfMoria L' <|operatore passa il valore alla funzione, piuttosto che passare la funzione al valore - il tuo esempio di codice funzionerebbe invece con l' |>operatore.
Tom Galvin,

@Quackmatic Oh. Non sapevo che esistessero più versioni (non ho usato molto F #).
BalinKingOfMoria Ripristina CM

18

Per me, penso che la risposta sia (a) la pulizia, come ha detto Don ; e (b) trovo che quando sto modificando il codice, la mia funzione potrebbe finire in uno stile senza punti, e quindi tutto ciò che devo fare è cancellare l'ultimo $invece di tornare indietro e cambiare tutto. Un punto minore, certamente, ma una gentilezza.


Sì, questo è un buon motivo per scriverlo in quel modo, se vuoi finire in una composizione di funzioni, allora è più veloce. :) Sì per salvare i tasti, ma soprattutto il tempo. +1
Robert Massaioli,

13

C'è una discussione interessante di questa domanda su questo thread di haskell-cafe . Apparentemente esiste un punto di vista di minoranza che sostiene che la giusta associatività di "$ è semplicemente sbagliata" , e scegliere f . g . h $ xoltre f $ g $ h $ xè un modo per aggirare il problema.


Ehi, hai trovato un'intera discussione sulla questione. È fantastico, sto leggendo ora. +1
Robert Massaioli,


Nota che $!ha anche la giusta associatività "chiaramente sbagliata" - Perché è $! operatore associativo di destra? .
imz - Ivan Zakharyaschev,

3

È solo una questione di stile. Tuttavia, il modo in cui il libro lo fa ha più senso per me. Compone tutte le funzioni e quindi lo applica al valore.

Il tuo metodo sembra strano e l'ultimo $non è necessario.

Tuttavia, non importa davvero. In Haskell, di solito ci sono molti, molti modi corretti per fare la stessa cosa.


3
Non ho notato che gli ultimi $ non erano necessari ma avrei dovuto. Grazie e lo lascerò lì in modo che la gente sappia cosa significa questo commento.
Robert Massaioli,

0

Mi rendo conto che questa è una domanda molto antica, ma penso che ci sia un altro motivo per questo che non è stato menzionato.

Se si dichiara una nuova funzione senza punti f . g . h, il valore che si passa verrà automaticamente applicato. Tuttavia, se scrivi f $ g $ h, non funzionerà.

Penso che il motivo per cui l'autore preferisce il metodo di composizione è perché porta a una buona pratica di creazione di funzioni.

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.