web-dev-qa-db-fra.com

Composition de Haskell (.) Par rapport à l'opérateur avant du tube de F # (|>)

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?

94
Ben Lings

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.

61
Brian

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.

82
Ganesh Sittampalam

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.

42

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

30
AshleyF

Si vous voulez utiliser le |> De F # dans Haskell, alors dans Data.Function est l'opérateur & (Depuis base 4.8.0.0).

22
jhegedus

Composition de gauche à droite à Haskell

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.

16
sastanin

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.

15
spookylukey

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:

  • Inférence de type de gauche à droite sans sous-types structurels.
  • La restriction de valeur.

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.

13
Jon Harrop

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.


Comparer |> 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. ⊙﹏⊙ |||

Humanisation

|> 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

Comment la programmation fonctionnelle en langage orienté objet?

veuillez visiter http://reactivex.io/

Support Informatique :

  • Java: RxJava
  • JavaScript: RxJS
  • C #: Rx.NET
  • C # (Unité): UniRx
  • Scala: RxScala
  • Clojure: RxClojure
  • C++: RxCpp
  • Lua: RxLua
  • Rubis: Rx.rb
  • Python: RxPY
  • Allez: RxGo
  • Groovy: RxGroovy
  • JRuby: RxJRuby
  • Kotlin: RxKotlin
  • Swift: RxSwift
  • PHP: RxPHP
  • Elixir: réactif
  • Fléchette: RxDart
2
lihansey

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.

1
tib