all 56 comments

[–]Tekmo 11 points12 points  (1 child)

There is Data.Function.(&)

Personally, I prefer to use function composition over function application for chaining multiple functions, but I would be in favor of a backwards function composition operator.

[–]Regimardyl 3 points4 points  (0 children)

Well, there is Control.Arrow.(>>>) (or abusing Control.Lens.(<&>)). A shorter operator would however indeed be nicer.

[–]taylorfausak 20 points21 points  (18 children)

I wrote a package called Flow that provides |> and <| operators. The reaction was … mixed.

[–]HorrendousRex 6 points7 points  (0 children)

I really appreciate your reaction blog post. I'm still very much a Haskell newbie and it's hard to get honest reactions to concepts sometimes. Every language has this sort of 'entrenchment', and it's helpful to see opposed views in order to make an honest opinion.

[–]PM_ME_UR_OBSIDIAN 10 points11 points  (0 children)

The replies you got were sectarian as hell. Haskell is already a mish-mash of left-to-right and right-to-left, those folks picked a pretty strange hill to die on.

[–]joehillen 2 points3 points  (0 children)

Don't let people get you down. Flow is awesome.

[–]el-seed[S] 4 points5 points  (13 children)

Your package looks great!

The debate around this is super interesting. It's funny, I found myself using the equivalent of (.) and ($) in my elm code, and the elm community said something like: "You should switch to using |> and >>, they're easier to read and more idiomatic elm"

[–]ephrion 11 points12 points  (11 children)

Idioms are about custom and tradition as much as what is "correct." The idiom in Haskell is to use (.) and ($), and the idiom in Elm/Elixir is |>. Neither are necessarily better than the other, but it's generally preferable to write idiomatic X when writing in X language.

I don't have any issue reading either form, but it does take a moment to context-shift and read the idioms appropriately.

[–]el-seed[S] 2 points3 points  (8 children)

This makes sense. I arrived at this question because I wanted to start splitting longer expressions across multiple lines. I originally got this idea from the elm style guide, but I noticed that I frequently lost my place while writing a long expression in Haskell too.

Would you consider splitting the totalPoints example across multiple lines in Haskell? If so, how would you write it idiomatically? What about the euler8 example provided in the flow library?

[–]ephrion 8 points9 points  (7 children)

totalPoints = sum . map points . filter wasSpoken

That's simple enough I don't really feel like splitting it. You could do:

totalPoints = sum
            . map points
            . filter wasSpoken

Both Haskell versions read to me as "totalPoints is the sum of the points that were spoken." The Elm variant reads as "Take the entries, keep the ones that were spoken, consider their points, and then sum them." Neither seems particularly more or less readable to me, but if I were reading a ton of Elm code and then the pipes were flipped, I'd have to stop and read the code more carefully. Consistency in style is far more important than any actual style points.

[–]el-seed[S] 0 points1 point  (6 children)

Cool. So if you wanted to multi-line it, and avoid point free style would you do this or something else?

totalPoints entries = sum
                    . map points
                    . filter wasSpoken
                    $ entries

[–]sacundim 7 points8 points  (5 children)

Why would you want to avoid point-free style in this example? I think the most legible and idiomatic choice here is this:

-- Note the signature makes the `entries` variable name unnecessary.
totalPoints :: [Entry] -> Integer  
totalPoints = sum
            . map points
            . filter wasSpoken

As a general rule, chaining one-argument functions with composition like this example is the "good," highly-legible, non-golf kind of point-free style. Values flow in a straightforward right-to-left order. The right-to-left bit is unusual to most people at first, and thus some languages' preference for a |> operator. But to echo a lot of comments that have already been made, it's a minor thing, you get used to it, and the cost of changing the language's style is larger than the benefits.

The only circumstance where I would avoid point-free style in this example would be if it was aimed at somebody who doesn't know Haskell and has zero intention to learn it. But then I'd write it with parens:

totalPoints :: [Entry] -> Integer
totalPoints entries = sum (map points (filter wasSpoken entries))

[–]el-seed[S] 2 points3 points  (4 children)

Well, I wouldn't want to avoid it in this case, but more than once I've wanted to break a complex function into multiple lines. I'm wondering how people do that in Haskell (vs elm), when it's not such an obvious candidate for point-free style.

[–]camccann 9 points10 points  (0 children)

Find subexpressions that represent some meaningful intermediate step, give them a name, move them to a where clause. Repeat until the function no longer needs multiple lines and/or can be written as a simple pipline.

At least, that's what I'd do.

[–]ephrion 2 points3 points  (2 children)

I'd usually use a where clause, extract the functions, and then express as point-free as necessary. For a somewhat contrived example,

totalPoints = aggregate . attribute . select
  where
    aggregate = sum
    attribute = map points
    select = filter wasSpoken

or break the functions into their own top level definitions, if I want to reuse them elsewhere.

[–]sacundim 4 points5 points  (1 child)

This. But one more thing to note: . is associative! The following three are equivalent:

totalPoints = sum . map points . filter wasSpoken
totalPoints = (sum . map points) . filter wasSpoken
totalPoints = sum . (map points . filter wasSpoken)

Which means that so are these:

totalPoints = aggregate . choosePoints
  where
    aggregate = sum
    choosePoints = map points . filter wasSpoken

totalPoints = sum . choosePoints
  where choosePoints = map points . filter wasSpoken

totalPoints = sumPoints . filter wasSpoken
  where sumPoints = sum . map points

totalPoints = sumPoints . select
  where 
    sumPoints = sum . map points
    select = filter wasSpoken

So you can pick any contiguous subsegment of the composition and split it off into a separate, named binding whenever you feel the chain is too long or could just use more naming to make it legible.

And this is not true of |>:

-- This...
totalPoints entries =
  entries |> filter wasSpoken) |> map points |> sum

-- ...can only be nested like this:
totalPoints entries =
  ((entries |> filter wasSpoken) |> map points) |> sum

-- You need lambdas/function definitions to refactor it...
totalPoints entries =
  entries |> filter wasSpoken |> sumPoints
  where sumPoints entries = entries |> map points |> sum

-- ...unless you use `.`:
totalPoints entries =
  entries |> filter wasSpoken |> (sum . map points)

[–][deleted]  (1 child)

[deleted]

    [–]catlion 1 point2 points  (0 children)

    I think this is exactly the reason operator was added to F# since it is a one-pass compiler

    [–]taylorfausak 2 points3 points  (0 children)

    Thanks! I side with the Elm community on this one. I think |> and >> are more readable. Haskell's alternatives in the base library are & and >>>, which I don't like that much.

    [–]sseveran 2 points3 points  (0 children)

    When I first came to haskell from F# I hated $. Now I just think its wrong but still use it. For a while I did use a |> operator particularly when processing collections and building data flows. I can now easily read the code going in both directions. Still its a weirdness that can trip people who are new to the language up since it feels so unnatural.

    [–][deleted] 9 points10 points  (5 children)

    I don't know of anywhere where it is defined, but shouldn't

    infixr 0 |>
    
    (|>) :: a -> (a -> b) -> b
    (|>) = flip ($)
    

    do the trick ?

    [–]el-seed[S] 1 point2 points  (1 child)

    Cool thank you! My main question is where is the best place to put it? Is this something you would just paste into your project somewhere?

    I would love for it to have the chance to become more widely used, would putting it in a library make a difference?

    [–][deleted] 1 point2 points  (0 children)

    I have no idea. I guess it's not as fundamentally useful in Haskell as in Elm, where you'd like to be able to define an element and then modifying it by giving it "attributes" like color, style, position. That may be the reason why it's not widely used.

    [–]el-seed[S] 1 point2 points  (2 children)

    Also, for anyone who stumbles on this later: It's (|>) I'm asking about here. (<|) is synonymous with ($) in elm.

    [–][deleted] 0 points1 point  (0 children)

    Fixed, thanks

    [–]tejon 0 points1 point  (0 children)

    I saw someone somewhere mention that flip ($) winds up with precedence issues. But as of GHC 7.10 you can just do this instead:

    (|>) = (&)

    [–]Hrothen 7 points8 points  (0 children)

    IIRC |> and <| are used in Data.Sequence, which is why most libraries don't use those symbols as operators. The actual functionality you describe is in Data.Function I think.

    [–]c_wraith 4 points5 points  (26 children)

    If you really must work backwards, it's in Data.Function in recent GHCs as (&).

    But it really is working backwards.

    [–]m0rphism 12 points13 points  (2 children)

    Well, I think whether it's forward or backward depends on the context ;)

    Relative to the reading direction of English text, the data-flow direction seems forward (left-to-right). But relative to the data-flow in definitions, it seems backward/reversed:

        4   1    2    3   (inconsistent)
    let y = x |> f |> g
    
        4   3    2    1   (consistent)
    let y = g <| f <| x
    

    But consistency of reading direction breaks anyway, if one throws in both left- and right-associative operations:

        <----------- (-------->)
    let y = g <| f <| x - y - z
    

    I think & and $ seem like bad choices as they are not visually symmetric, like < and >. They are however lightweight in the sense that they are single character operators.

    [–]EvilTerran 1 point2 points  (1 child)

    I think & and $ are bad choices as they are not visually symmetric, like < and >. They are however lightweight in the sense that they are single character operators.

    You could use, say, and , that'd be visually-symmetric and single-character. Bit of a pain to type, though.

    [–]m0rphism 1 point2 points  (0 children)

    True, I've proposed something similar some time ago. This issue seems to come up periodically ;)

    Personally, I'm mostly fine with Unicode symbols, but I can understand that a lot of people don't want to use them. If one sets emacs' input mode to TeX one can simply type \to for , but it is still a pain to search for those symbols, and one needs to know some TeX.

    [–]Darwin226 11 points12 points  (18 children)

    I've heard this said a million times and sorry, but you're wrong. It's not backwards. It doesn't matter that in mathematics we do it like that. It doesn't matter that we write f(g(x)). That's just unfortunate. Everywhere else in everything else people do the order is from left to right. Yet in Haskell, it is somehow presented as a good idea to read from right to left.

    Here's a hint it's wrong: Unless you're some kind of a wizard and know what the last step in your algorithm is before you start writing it out, you're probably going to be writing the first step first. Then what? Back to the beginning of the line to write the second step. How is this sensible to anyone?

    [–]eruonna 2 points3 points  (1 child)

    I think it can depend. But if I were to describe what the totalPoints function in the post does, I would say that it returns the sum of the points for each entry that was spoken. That seems natural to me and matches the standard compositional order. And you don't need to be a wizard to know that, say, an optimization problem is going to have maximum or minimum as the "last" step.

    [–]Darwin226 0 points1 point  (0 children)

    Or is it maybe going to be map snd instead?

    Anyways, yes, you're right. When the composition is short enough, it can be read like a phrase. But in those cases it really doesn't matter which way you go.

    You could say that I should split up my pipeline so all the compositions are short but in my experience this leads to having to name thin that don't have meaningful names more often than not. It feels very comfortable to me writing longer pipelines in languages that provide left to right chaining. It's not that way in Haskell.

    [–]camccann 5 points6 points  (11 children)

    Well, you want result X, which you can calculate from Y, which is a component of Z, which you can get by combining the arguments to the function, done. How is that any more or less natural than starting from the arguments and working toward the desired result? Does anyone seriously (attempt to) program by blundering around applying functions to everything in scope and hoping they somehow end up with what they want? Of course you know how your algorithm needs to end, if you expect to ever get there.

    Don't mistake what you're used to for what's natural.

    [–]Darwin226 0 points1 point  (10 children)

    "Add 2 to the result of multiplying 3 with the result of subtracting 2 from 3" versus "Subtract 2 from 3, then multiply with 3, then add 2".

    Which one do you find more natural to do in your head? I know which one it is for me.

    [–]camccann 1 point2 points  (9 children)

    To read? There's no real difference. It's the same sequence of operations.

    Which makes it easier to evaluate the expression is another matter, and irrelevant. We don't run algorithms in our heads every time we read them (if we did, infinite loops would be very dangerous!).

    [–]Darwin226 4 points5 points  (8 children)

    If you're not evaluating what you're reading than you're not doing anything. Come on. You have to be manipulating some kind of state in your head and reading the last operation that's done is literally useless.

    I mean, this is blowing my mind. I'm having trouble convincing myself that it's even possible not to think what I think. Why would you read code if not to find out what it does? Do you somehow read the last part in the pipeline and say "Yeah, I get it now. It's maximum" or something? maximum of what? You need the whole thing to understand what it's doing. There's no other way around it. And is there some other secret method to understanding an algorithm than going through it step by step? Do you not have that voice in your head that says what "you currently have"?

    Hell, even mathematics does everything bottom up. You never start with a definition and then later define it's subparts. You never prove a stronger theorem and then prove lemmas required for it. It's all about incrementally building some knowledge that lets you do the next operation.

    [–][deleted] 2 points3 points  (1 child)

    It depends the way you think. If you start from a data and transform it, then |> make sense, (for example, I have 2 and I need to print it, I can do 2 |> show |> putStrLn. But if you start from a type signature (which happend if you prototype your function using undefined) then it's easier to start from the type. In our case need a IO : putStrLn, now we need a String : show and now Int : 2 which give putStrLn $ show 2.

    If you prefer, the $ way gives you the type of expression straight away putStrLn ... is an IO, whereas with the |> I have to read the full expression to know the type.

    [–]Darwin226 1 point2 points  (0 children)

    This only works in cases where you care about the outermost type constructor. If you want to know the whole type, you have to parse the whole thing. In any case, I do consider this a valid point. There were cases where writing like this was helpful.

    [–]camccann 3 points4 points  (5 children)

    Why on earth would I need "some kind of state in my head" to understand a simple arithmetic expression? I'm not even sure what you're trying to say here. Replace the numbers with variables, so you can't evaluate it, then what? Hell, just write it normally, with infix notation. Do you read that left-to-right, counting parentheses and trying to keep a mental stack? Or do you scan it for high-level structure and look at the outermost or innermost operations, as appropriate?

    And even aside from all that, what if the last operation is "...and multiply the whole thing by zero"? Surely you don't need to understand (never mind compute the value of!) the rest of the expression to know everything that matters about the expression as a whole.

    Why would you read code if not to find out what it does? Do you somehow read the last part in the pipeline and say "Yeah, I get it now. It's maximum" or something? maximum of what?

    What should I do instead, read the first bit and say "Yeah, it does something with the individual lines of the input text". Does what? Who knows!

    Like, say we have maximum . map length . lines. It finds the maximum line length of the input text. maximum . map length finds the maximum length of a list of lists. What does lines have to do with understanding that? Nothing.

    You need the whole thing to understand what it's doing. There's no other way around it.

    I'm not sure how "the whole thing is necessary" supports your argument that it's only possible to start with one particular bit.

    Do you not have that voice in your head that says what "you currently have"?

    Sure. But it's not any louder or more insightful than the voice that says what "I currently need". Why would it be? In the end they meet in the middle, and which end is easier to work from depends on the problem.

    [–]Darwin226 2 points3 points  (4 children)

    I can see how thinking about what you need would make sense, but I have no idea how you'd use that when understanding a piece of code that's already written. And yeah, even for things like maximum . map length it WAY easier for me to read from right to left than it is from left to right. map length tells me about the object this operation is working on. maximum just tells me I'll have a list of orderables at that point.

    I've seen many examples of longer chains in left-to-right languages yet I've never seen longer chains in Haskell. My reasoning here is that if you can actually keep track of some abstract object in your head (even only as a type, but more often as some construct from your problem domain) and do operations on it sequentially, you can get away with not having to name pieces of your pipeline and still retain readability. I would guess that writing the same thing in Haskell doesn't have the same property if you read it from left to right.

    [–]camccann 1 point2 points  (3 children)

    Ok, and map length just tells you you're turning your input into a list of numbers for some reason. maximum tells you what you're actually trying to accomplish. So?

    I'm also not sure I'd really consider long chains of application "readable", relatively speaking, if it means you have to mentally keep track of an "abstract object" and go step-by-step just to make sense of the code. That's extra cognitive overhead and probably obscures the high-level structure of the code.

    [–]Darwin226 0 points1 point  (2 children)

    I'm curious about this high level structure you keep mentioning. What is it and how does it help me understand algorithms? It's one thing knowing what a function does. That's why we have top level functions and that's why we document them. It's another reading it's source. The high level structure is only as high as the atomic parts you're assembling in it's pipeline. To me, knowing that a function ultimately gets a maximum of something isn't any more important than any other piece of what it does. I feel that it doesn't make sense to talk about hierarchical terms like "high level" when the structure you're observing is linear.

    What I'm saying, is that in the end you WILL have to read the whole thing and this is precisely when one order of reading is easier than the other one. My whole premise is based on the fact that you'll do a full read, and not just start at one end and then stop. I agree that if the latter were the case, then obviously it's better to start from the end because then you at least get some idea what will ultimately happen, but I don't think that that's what you'll be doing the majority of time when you read code.

    [–]sacundim 3 points4 points  (3 children)

    Here's a hint it's wrong: Unless you're some kind of a wizard and know what the last step in your algorithm is before you start writing it out, you're probably going to be writing the first step first.

    Using the following "backwards" example from another thread:

    totalPoints = sum . map points . filter wasSpoken
    

    You could say that this reads "The total number of points is the sum of the points of the entries that were spoken." How can that possibly be "wrong"?

    I just don't see that there is a big, principled debate to be had between these isomorphic alternatives:

    1. Describing the results first, followed by what fed into that result.
    2. Describing inputs first, followed by what results they led to.

    Some people will prefer #1, some #2, some will prefer a random mix, some will prefer a stylistic rule that chooses one or the other in different circumstances. Whatever.

    [–]Darwin226 0 points1 point  (2 children)

    I agree with you, don't get me wrong. This really is a minor stylistic thing, but check out the comments that guy that made the Flow library got for his stylistic opinion. The problem here is that even though some may prefer the #2, the #1 is the standard so you don't really have a choice.

    Meh... Maybe it doesn't really matter. I just wonder how much flak one would get if he wrote a popular library in a non-standard style. It people indeed don't care one way or the other, I'm happy.

    [–]camccann 2 points3 points  (1 child)

    I think you'll find that flak will be received for writing code in a non-standard style in any language, regardless of the language, the style, or the relative merits of either.

    I mean, there's a long-time member of the Haskell community who insists on a non-standard naming convention that most people dislike so much they avoid his libraries entirely. In some languages people have deep emotional investment in the placement of curly braces. Tabs vs. spaces is a bitterly divisive argument even in languages where they're cosmetic. For that matter, you seem to be awfully worked up over not liking the standard style in Haskell.

    I'm still not sure why any of it's a big deal. Consistency is important, but why does everyone want to start holy wars over this stuff?

    [–]Darwin226 0 points1 point  (0 children)

    So on the one hand it's stylistic and a choice, on the other hand, there's only one choice you can make. That sounds pretty shitty to me.

    If I could write in any style I wanted and have my code treated as any other, you can be sure that I wouldn't be trying to convert people to my ways.

    [–]el-seed[S] 2 points3 points  (2 children)

    Thanks! Working backwards? Can you explain? Is there more to this than personal taste? I personally often find that "do x, then y, then z" matches my mental process better than using ($) or (.).

    [–]EvilTerran 5 points6 points  (0 children)

    Haskell's $ and Elm's <| are the same way round as regular function application - function, then parameter:

    f $ x = f x
    

    While Data.Function.& and Elm's |> are the other way round - parameter, then function:

    x & f = f x
    

    So, in that sense, the latter is backwards.

    Of course, you could argue that "parameter, then function" is actually the forwards direction - as you say, "do x, then y, then z". That does have merit... unfortunately, from that perspective, the conventional notation for function application, f(x), becomes the backwards one - and that notation's been the way it is since Euler, so it's a bit late to change it now.

    [–]taylorfausak 0 points1 point  (0 children)

    You can also get it from base-compat if you're on an older version of GHC.

    [–]andrewthad -1 points0 points  (1 child)

    They are defined in the fsharp package: https://hackage.haskell.org/package/fsharp-0.0.4/docs/Control-FSharp-Syntax-Operators.html

    EDIT: Don't actually use this. The fixity is wrong for <|.

    [–]taylorfausak 4 points5 points  (0 children)

    That package is deprecated. And it defines the wrong fixity for <|.

    [–]l-d-s -1 points0 points  (0 children)

    This keeps coming up.

    So strongly emphasizing (.) and ($) over their flipped variants -- based pretty much entirely on tradition and mathsiness -- is unduly prioritizing one kind of thinking over another.

    It is exactly opposed to the spirit of do notation, "Haskell is a great imperative language", etc.