Haskell composition (.) vs F#'s pipe forward operator (|>)

前端 未结 10 1611
鱼传尺愫
鱼传尺愫 2020-12-04 07:36

In F#, use of the the pipe-forward operator, |>, is pretty common. However, in Haskell I\'ve only ever seen function composition, (.), being us

相关标签:
10条回答
  • 2020-12-04 07:41

    I think we're confusing things. Haskell's (.) is equivalent to F#'s (>>). Not to be confused with F#'s (|>) which is just inverted function application and is like Haskell's ($) - reversed:

    let (>>) f g x = g (f x)
    let (|>) x f = f x
    

    I believe Haskell programmers do use $ often. Perhaps not as often as F# programmers tend to use |>. On the other hand, some F# guys use >> to a ridiculous degree: http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx

    0 讨论(0)
  • 2020-12-04 07:41

    I have seen >>> being used for flip (.), and I often use that myself, especially for long chains that are best understood left-to-right.

    >>> is actually from Control.Arrow, and works on more than just functions.

    0 讨论(0)
  • 2020-12-04 07:46

    Left-to-right composition in Haskell

    Some people use left-to-right (message-passing) style in Haskell too. See, for example, mps library on Hackage. An example:

    euler_1 = ( [3,6..999] ++ [5,10..999] ).unique.sum
    

    I think this style looks nice in some situations, but it's harder to read (one needs to know the library and all its operators, the redefined (.) is disturbing too).

    There are also left-to-right as well as right-to-left composition operators in Control.Category, part of the base package. Compare >>> and <<< respectively:

    ghci> :m + Control.Category
    ghci> let f = (+2) ; g = (*3) in map ($1) [f >>> g, f <<< g]
    [9,5]
    

    There is a good reason to prefer left-to-right composition sometimes: evaluation order follows reading order.

    0 讨论(0)
  • 2020-12-04 07:52

    In F# (|>) is important because of the left-to-right typechecking. For example:

    List.map (fun x -> x.Value) xs
    

    generally won't typecheck, because even if the type of xs is known, the type of the argument x to the lambda isn't known at the time the typechecker sees it, so it doesn't know how to resolve x.Value.

    In contrast

    xs |> List.map (fun x -> x.Value)
    

    will work fine, because the type of xs will lead to the type of x being known.

    The left-to-right typechecking is required because of the name resolution involved in constructs like x.Value. Simon Peyton Jones has written a proposal for adding a similar kind of name resolution to Haskell, but he suggests using local constraints to track whether a type supports a particular operation or not, instead. So in the first sample the requirement that x needs a Value property would be carried forward until xs was seen and this requirement could be resolved. This does complicate the type system, though.

    0 讨论(0)
  • 2020-12-04 07:53

    I am being a little speculative...

    Culture: I think |> is an important operator in the F# "culture", and perhaps similarly with . for Haskell. F# has a function composition operator << but I think the F# community tends to use points-free style less than the Haskell community.

    Language differences: I don't know enough about both languages to compare, but perhaps the rules for generalizing let-bindings are sufficiently different as to affect this. For example, I know in F# sometimes writing

    let f = exp
    

    will not compile, and you need explicit eta-conversion:

    let f x = (exp) x   // or x |> exp
    

    to make it compile. This also steers people away from points-free/compositional style, and towards the pipelining style. Also, F# type inference sometimes demands pipelining, so that a known type appears on the left (see here).

    (Personally, I find points-free style unreadable, but I suppose every new/different thing seems unreadable until you become accustomed to it.)

    I think both are potentially viable in either language, and history/culture/accident may define why each community settled at a different "attractor".

    0 讨论(0)
  • 2020-12-04 07:56

    This is my first day to try Haskell (after Rust and F#), and I was able to define F#'s |> operator:

    (|>) :: a -> (a -> b) -> b
    (|>) x f = f x
    infixl 0 |>
    

    and it seems to work:

    factorial x =
      case x of
        1 -> 1
        _ -> x * factorial (x-1)
    
    main =     
        5 |> factorial |> print
    

    I bet a Haskell expert can give you an even better solution.

    0 讨论(0)
提交回复
热议问题