Risposte:
Sono un po 'speculativo ...
Cultura : penso |>
sia un operatore importante nella "cultura" di F #, e forse in modo simile .
per Haskell. F # ha un operatore di composizione delle funzioni, <<
ma penso che la comunità di F # tenda a usare lo stile senza punti meno della comunità Haskell.
Differenze linguistiche : non so abbastanza su entrambe le lingue per confrontarle, ma forse le regole per generalizzare i let-binding sono sufficientemente diverse da influire su questo. Ad esempio, so che a volte in F # scrivo
let f = exp
non verrà compilato e avrai bisogno di una conversione eta esplicita:
let f x = (exp) x // or x |> exp
per farlo compilare. Questo allontana anche le persone dallo stile compositivo / senza punti e verso lo stile di pipeline. Inoltre, l'inferenza del tipo F # a volte richiede il pipelining, in modo che un tipo noto venga visualizzato a sinistra (vedere qui ).
(Personalmente, trovo illeggibile lo stile senza punti, ma suppongo che ogni cosa nuova / diversa sembri illeggibile finché non ci si abitua.)
Penso che entrambi siano potenzialmente praticabili in entrambe le lingue, e la storia / cultura / incidente può definire il motivo per cui ogni comunità si è stabilita in un diverso "attrattore".
.
e $
, quindi le persone continuano a usarli.
In F # (|>)
è importante a causa del controllo del tipo da sinistra a destra. Per esempio:
List.map (fun x -> x.Value) xs
generalmente non esegue il typecheck, perché anche se il tipo di xs
è noto, il tipo dell'argomento x
del lambda non è noto nel momento in cui il typechecker lo vede, quindi non sa come risolvere x.Value
.
In contrasto
xs |> List.map (fun x -> x.Value)
funzionerà bene, perché il tipo di xs
porterà al tipo di x
essere conosciuto.
Il controllo del tipo da sinistra a destra è richiesto a causa della risoluzione del nome coinvolta in costrutti come x.Value
. Simon Peyton Jones ha scritto una proposta per aggiungere un tipo simile di risoluzione dei nomi a Haskell, ma suggerisce di utilizzare i vincoli locali per tracciare se un tipo supporta o meno una particolare operazione. Quindi nel primo campione il requisito che x
necessita di una Value
proprietà sarebbe stato portato avanti fino a quando non xs
fosse stato visto e questo requisito potrebbe essere risolto. Tuttavia, questo complica il sistema dei tipi.
Altre speculazioni, questa volta dal lato prevalentemente Haskell ...
($)
è il capovolgimento di (|>)
, e il suo utilizzo è abbastanza comune quando non è possibile scrivere codice senza punti. Quindi il motivo principale che (|>)
non viene utilizzato in Haskell è che il suo posto è già stato preso($)
.
Inoltre, parlando di un po 'di esperienza F #, penso che (|>)
sia così popolare nel codice F # perché assomiglia alla Subject.Verb(Object)
struttura di OO. Poiché F # mira a un'integrazione funzionale / OO uniforme, Subject |> Verb Object
è una transizione piuttosto fluida per i nuovi programmatori funzionali.
Personalmente, mi piace anche pensare da sinistra a destra, quindi uso (|>)
in Haskell, ma non credo che molte altre persone lo facciano.
Data.Sequence.|>
, ma $>
sembra ragionevole evitare conflitti lì. Onestamente, ci sono solo così tanti operatori di bell'aspetto, quindi userei solo |>
per entrambi e gestirò i conflitti caso per caso. (Inoltre sarei tentato di fare solo uno pseudonimo Data.Sequence.|>
come snoc
)
($)
e (|>)
sono applicazione non composizione. I due sono correlati (come nota la domanda) ma non sono la stessa cosa (il tuo fc
è (Control.Arrow.>>>)
per le funzioni).
|>
realtà mi ricorda UNIX |
più di ogni altra cosa.
|>
in F # è che ha proprietà interessanti per IntelliSense in Visual Studio. Digita |>
e ottieni un elenco di funzioni che possono essere applicate al valore a sinistra, simile a quello che accade quando si digita .
dopo un oggetto.
Penso che stiamo confondendo le cose. Haskell's ( .
) è equivalente a F # ( >>
). Da non confondere con F # 's ( |>
) che è solo un'applicazione funzione invertita ed è come Haskell ( $
) - invertita:
let (>>) f g x = g (f x)
let (|>) x f = f x
Credo che i programmatori Haskell usino $
spesso. Forse non così spesso come i programmatori F # tendono a usare |>
. D'altra parte, alcuni ragazzi di F # usano >>
in misura ridicola: http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx
$
operatore di Haskell - invertito, puoi anche facilmente definirlo come: a |> b = flip ($)
che diventa equivalente alla pipeline di F #, ad esempio puoi farlo[1..10] |> map f
.
) sia uguale a ( <<
), mentre ( >>
) è la composizione inversa. Questo è il ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3
vs( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
.
sia equivalente a >>
. Non so se F # ha <<
ma sarebbe l'equivalente (come in Elm).
Se vuoi usare F # |>
in Haskell, allora in Data.Function c'è l' &
operatore (da base 4.8.0.0
).
&
sopra |>
? Mi sembra che |>
sia molto più intuitivo e mi ricorda anche l'operatore pipe di Unix.
Alcune persone usano lo stile da sinistra a destra (passaggio di messaggi) anche in Haskell. Vedi, per esempio, mps biblioteca Hackage. Un esempio:
euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum
Penso che questo stile sia carino in alcune situazioni, ma è più difficile da leggere (bisogna conoscere la libreria e tutti i suoi operatori, anche la ridefinizione (.)
disturba).
Ci sono anche operatori di composizione da sinistra a destra e da destra a sinistra in Control.Category , parte del pacchetto base. Confronta >>>
e <<<
rispettivamente:
ghci> :m + Control.Category
ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
[9,5]
A volte c'è una buona ragione per preferire la composizione da sinistra a destra: l'ordine di valutazione segue l'ordine di lettura.
Ho visto >>>
essere usato perflip (.)
, e spesso lo uso io stesso, specialmente per catene lunghe che sono meglio comprese da sinistra a destra.
>>>
è in realtà da Control.Arrow e funziona su più di semplici funzioni.
>>>
è definito in Control.Category
.
A parte lo stile e la cultura, questo si riduce all'ottimizzazione del design del linguaggio per il codice puro o impuro.
L' |>
operatore è comune in F # in gran parte perché aiuta a nascondere due limitazioni che appaiono con codice prevalentemente impuro:
Si noti che la prima limitazione non esiste in OCaml perché il sottotipo è strutturale anziché nominale, quindi il tipo strutturale è facilmente raffinato tramite l'unificazione man mano che l'inferenza del tipo progredisce.
Haskell prende un compromesso diverso, scegliendo di concentrarsi su un codice prevalentemente puro in cui queste limitazioni possono essere eliminate.
Penso che l'operatore pipe forward ( |>
) di F # dovrebbe vs ( & ) in haskell.
// pipe operator example in haskell
factorial :: (Eq a, Num a) => a -> a
factorial x =
case x of
1 -> 1
_ -> x * factorial (x-1)
// terminal
ghic >> 5 & factorial & show
Se non ti piace l' &
operatore ( ), puoi personalizzarlo come F # o Elixir:
(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 1 |>
ghci>> 5 |> factorial |> show
Perché infixl 1 |>
? Vedi il documento in Data-Function (&)
infixl = infix + associatività sinistra
infixr = infix + associatività a destra
( .
) significa composizione della funzione. Significa (fg) (x) = f (g (x)) in matematica.
foo = negate . (*3)
// ouput -3
ghci>> foo 1
// ouput -15
ghci>> foo 5
è uguale
// (1)
foo x = negate (x * 3)
o
// (2)
foo x = negate $ x * 3
L' $
operatore ( ) è definito anche in Funzione dati ($) .
( .
) viene utilizzato per creare Hight Order Function
o closure in js
. Vedi esempio:
// (1) use lamda expression to create a Hight Order Function
ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]
// (2) use . operator to create a Hight Order Function
ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]
Wow, Less (code) è meglio.
|>
e.
ghci> 5 |> factorial |> show
// equals
ghci> (show . factorial) 5
// equals
ghci> show . factorial $ 5
È la differenza tra left —> right
e right —> left
. ⊙﹏⊙ |||
|>
ed &
è meglio di.
perché
ghci> sum (replicate 5 (max 6.7 8.9))
// equals
ghci> 8.9 & max 6.7 & replicate 5 & sum
// equals
ghci> 8.9 |> max 6.7 |> replicate 5 |> sum
// equals
ghci> (sum . replicate 5 . max 6.7) 8.9
// equals
ghci> sum . replicate 5 . max 6.7 $ 8.9
visitare http://reactivex.io/
Supporto IT :
Questo è il mio primo giorno per provare Haskell (dopo Rust e F #) e sono stato in grado di definire l'operatore |> di F #:
(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 0 |>
e sembra funzionare:
factorial x =
case x of
1 -> 1
_ -> x * factorial (x-1)
main =
5 |> factorial |> print
Scommetto che un esperto Haskell può darti una soluzione ancora migliore.
x |> f = f x
&
è di Haskell|>
. Sepolto in profondità in questo filo e mi ci sono voluti alcuni giorni per scoprirlo. Lo uso molto, perché leggi naturalmente da sinistra a destra per seguire il tuo codice.