En F #, utilisation de l'opérateur pipe-forward, |>
, est assez courant. Cependant, dans Haskell, je n'ai vu que la composition des fonctions, (.)
, utilisé. Je comprends qu'ils sont liés , mais y a-t-il une raison linguistique pour laquelle le pipe-forward n'est pas utilisé dans Haskell ou est-ce autre chose?
Je suis un peu spéculatif ...
Culture : Je pense que |>
est un opérateur important dans la "culture" F #, et peut-être aussi avec .
pour Haskell. F # a un opérateur de composition de fonction <<
mais je pense que la communauté F # a tendance à utiliser style sans points moins que la communauté Haskell.
Différences de langue : Je ne connais pas suffisamment les deux langues pour comparer, mais peut-être que les règles de généralisation des liaisons de let sont suffisamment différentes pour affecter cela. Par exemple, je sais qu'en F # j'écris parfois
let f = exp
ne compilera pas, et vous avez besoin d'une conversion ETA explicite:
let f x = (exp) x // or x |> exp
pour le faire compiler. Cela éloigne également les gens du style sans points/compositionnel et vers le style de pipelining. De plus, l'inférence de type F # nécessite parfois un pipelining, de sorte qu'un type connu apparaît à gauche (voir ici ).
(Personnellement, je trouve le style sans points illisible, mais je suppose que chaque chose nouvelle/différente semble illisible jusqu'à ce que vous vous y habituiez.)
Je pense que les deux sont potentiellement viables dans l'une ou l'autre langue, et l'histoire/la culture/l'accident peut définir pourquoi chaque communauté s'est installée sur un "attracteur" différent.
Dans F # (|>)
est important en raison de la vérification typographique de gauche à droite. Par exemple:
List.map (fun x -> x.Value) xs
ne vérifie généralement pas la vérification typographique, car même si le type de xs
est connu, le type de l'argument x
du lambda n'est pas connu au moment où le vérificateur typographique le voit, donc il ne le fait pas sais pas comment résoudre x.Value
.
En revanche
xs |> List.map (fun x -> x.Value)
fonctionnera correctement, car le type de xs
entraînera la connaissance du type de x
.
La vérification de type de gauche à droite est requise en raison de la résolution de nom impliquée dans les constructions comme x.Value
. Simon Peyton Jones a écrit une proposition pour ajouter un type similaire de résolution de noms à Haskell, mais il suggère d'utiliser des contraintes locales pour suivre si un type prend en charge une opération particulière ou non, à la place. Ainsi, dans le premier exemple, l'exigence selon laquelle x
a besoin d'une propriété Value
serait reportée jusqu'à ce que xs
soit visible et cette exigence pourrait être résolue. Cela complique cependant le système de type.
Plus de spéculation, cette fois du côté majoritairement Haskell ...
($)
Est le flip de (|>)
, Et son utilisation est assez courante lorsque vous ne pouvez pas écrire de code sans point. Ainsi, la principale raison pour laquelle (|>)
N'est pas utilisé dans Haskell est que sa place est déjà prise par ($)
.
En outre, en parlant d'un peu d'expérience en F #, je pense que (|>)
Est si populaire dans le code F # car il ressemble à la structure Subject.Verb(Object)
d'OO. Puisque F # vise une intégration fonctionnelle/OO fluide, Subject |> Verb Object
Est une transition assez fluide pour les nouveaux programmeurs fonctionnels.
Personnellement, j'aime penser de gauche à droite aussi, donc j'utilise (|>)
Dans Haskell, mais je ne pense pas que beaucoup d'autres personnes le fassent.
Je pense que nous confondons les choses. Haskell (.
) est équivalent à F # (>>
). A ne pas confondre avec les F # (|>
) qui est juste une application de fonction inversée et qui ressemble à celle de Haskell ($
) - inversé:
let (>>) f g x = g (f x)
let (|>) x f = f x
Je pense que les programmeurs Haskell utilisent $
souvent. Peut-être pas aussi souvent que les programmeurs F # ont tendance à utiliser |>
. D'un autre côté, certains gars F # utilisent >>
à un degré ridicule: http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx
Si vous voulez utiliser le |>
De F # dans Haskell, alors dans Data.Function est l'opérateur &
(Depuis base 4.8.0.0
).
Certaines personnes utilisent également le style de gauche à droite (passage de message) dans Haskell. Voir, par exemple, mps bibliothèque sur Hackage. Un exemple:
euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum
Je pense que ce style a l'air bien dans certaines situations, mais il est plus difficile à lire (il faut connaître la bibliothèque et tous ses opérateurs, le (.)
Redéfini est aussi dérangeant).
Il existe également des opérateurs de composition de gauche à droite ainsi que de droite à gauche dans Control.Category , qui fait partie du package de base. Comparez respectivement >>>
Et <<<
:
ghci> :m + Control.Category
ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
[9,5]
Il y a une bonne raison de préférer parfois la composition de gauche à droite: l'ordre d'évaluation suit l'ordre de lecture.
J'ai vu >>>
Utilisé pour flip (.)
, et je l'utilise souvent moi-même, en particulier pour les longues chaînes qui sont mieux comprises de gauche à droite.
>>>
Est en fait de Control.Arrow et fonctionne sur plus que de simples fonctions.
Mis à part le style et la culture, cela revient à optimiser la conception du langage pour du code pur ou impur.
Le |>
L'opérateur est courant dans F # en grande partie parce qu'il aide à masquer deux limitations qui apparaissent avec un code principalement impur:
Notez que la première limitation n'existe pas dans OCaml car le sous-typage est structurel au lieu de nominal, de sorte que le type structurel est facilement affiné via l'unification à mesure que l'inférence de type progresse.
Haskell prend un compromis différent, choisissant de se concentrer sur un code principalement pur où ces limitations peuvent être levées.
Je pense que l'opérateur avant du tube de F # (|>
) devrait vs ( & ) dans 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
Si vous n'aimez pas (&
), vous pouvez le personnaliser comme F # ou Elixir:
(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 1 |>
ghci>> 5 |> factorial |> show
Pourquoi infixl 1 |>
? Voir la doc dans Data-Function (&)
infixl = infixe + associativité gauche
infixr = infixe + associativité droite
(.
) signifie la composition de la fonction. Cela signifie (f.g) (x f (g (x))) == en mathématiques.
foo = negate . (*3)
// ouput -3
ghci>> foo 1
// ouput -15
ghci>> foo 5
c'est égal
// (1)
foo x = negate (x * 3)
ou
// (2)
foo x = negate $ x * 3
($
) est également défini dans Data-Function ($) .
(.
) est utilisé pour créer Hight Order Function
ou closure in js
. Voir l'exemple:
// (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, moins (code) c'est mieux.
|>
et .
ghci> 5 |> factorial |> show
// equals
ghci> (show . factorial) 5
// equals
ghci> show . factorial $ 5
C'est la différence entre left —> right
et right —> left
. ⊙﹏⊙ |||
|>
et &
est mieux que .
car
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
veuillez visiter http://reactivex.io/
Support Informatique :
C'est mon premier jour pour essayer Haskell (après Rust et F #), et j'ai pu définir l'opérateur F # | |>:
(|>) :: a -> (a -> b) -> b
(|>) x f = f x
infixl 0 |>
et cela semble fonctionner:
factorial x =
case x of
1 -> 1
_ -> x * factorial (x-1)
main =
5 |> factorial |> print
Je parie qu'un expert Haskell peut vous donner une solution encore meilleure.