Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upList.append has flipped argument order #776
Comments
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
process-bot
Dec 5, 2016
Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it!
Here is what to expect next, and if anyone wants to comment, keep these things in mind.
process-bot
commented
Dec 5, 2016
|
Thanks for the issue! Make sure it satisfies this checklist. My human colleagues will appreciate it! Here is what to expect next, and if anyone wants to comment, keep these things in mind. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rgrempel
Dec 5, 2016
Contributor
One way of looking at this problem is that the word append does not carry quite enough meaning to clearly tell you which argument will be appended to which argument.
That is, given append : List a -> List a -> List a, is there anything about the word "append" that tells us whether the first argument will be appended to the second, or the second to the first? I suppose you might argue that there is potentially an intuition that the order of the results will match the order of the arguments, but I'm not really sure that is so -- that is, I don't think there is much of a natural intuition one way or the other.
For what append currently does, I wonder whether appendTo would be a better name. For instance, if I saw:
[ "foo", "bar" ]
|> List.appendTo [ "baz" ] ... then I think I would pretty unambiguously assume that I was going to end up with [ "baz", "foo", "bar" ].
What about the non-pipelined form, i.e.:
List.appendTo [ "baz" ] [ "foo", "bar" ]I think that [ "baz", "foo", "bar" ]would also be non-surprising in that case, though I'm not quite as sure that others would agree.
I suppose the question this raises is whether appendTo creates a stronger intuition about argument ordering than prepend does.
The other angle on this that would be worth considering is this. Leaving aside the proper name for the function for the moment, the prior question is whether the first argument ought to end up first in the final list, or second in the final list. It seems to me that this depends on whether you think the thing being carried along in the pipeline is more likely to end up first or last in the final list.
Now, typically (but not always) the thing being carried along in the pipeline (i.e. the second argument) is something you've computed, whereas the first argument is often a constant. (In your case, that is, the ["foo", "bar"] is often a result of a computation carried out in the pipeline, whereas ["baz"] is often a constant).
So, the question reduces itself to: are you more often appending a constant list to a computed value, or a computed value to a constant list?
I don't think I know the answer to that question -- it just seems like it might be a relevant angle on this.
|
One way of looking at this problem is that the word That is, given For what [ "foo", "bar" ]
|> List.appendTo [ "baz" ] ... then I think I would pretty unambiguously assume that I was going to end up with What about the non-pipelined form, i.e.: List.appendTo [ "baz" ] [ "foo", "bar" ]I think that I suppose the question this raises is whether The other angle on this that would be worth considering is this. Leaving aside the proper name for the function for the moment, the prior question is whether the first argument ought to end up first in the final list, or second in the final list. It seems to me that this depends on whether you think the thing being carried along in the pipeline is more likely to end up first or last in the final list. Now, typically (but not always) the thing being carried along in the pipeline (i.e. the second argument) is something you've computed, whereas the first argument is often a constant. (In your case, that is, the So, the question reduces itself to: are you more often appending a constant list to a computed value, or a computed value to a constant list? I don't think I know the answer to that question -- it just seems like it might be a relevant angle on this. |
rtfeldman
changed the title from
List.append is implemented as List.prepend
to
List.append has flipped argument order
Dec 5, 2016
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rtfeldman
Dec 5, 2016
Member
I've wanted both "prepend" and "append" behavior at different times, to be honest.
I really like the appendTo idea! It's clearer to me than either status quo or prepend, and has better long-term characteristics because it eliminates the desire to reintroduce append someday.
I've edited the OP to reflect my view that this is a better solution.
|
I've wanted both "prepend" and "append" behavior at different times, to be honest. I really like the I've edited the OP to reflect my view that this is a better solution. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
kuzux
Dec 27, 2016
append should really be nothing but a synonym for (++). Calling this behaviour append is just downright weird.
kuzux
commented
Dec 27, 2016
|
|
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
@kuzux what strikes you as weird about it? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
mgold
Dec 27, 2016
Contributor
I agree with @kuzux that if we have append it should be a synonym for (++), but this proposal is that we shouldn't have append. It's much less weird, IMHO, for appendTo to not be a synonym for (++). I support the revised proposal of renaming the function without changing the argument order.
|
I agree with @kuzux that if we have |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
Mouvedia
commented
Jan 1, 2017
|
Id support the couple |
rtfeldman commentedDec 5, 2016
•
edited
Edited 1 time
-
rtfeldman
edited Dec 5, 2016 (most recent)
tl;dr
List.appendshould be renamedList.appendTo, to reflect the actual order of its arguments.The Bug
Here is some Elm code:
Its output, surprisingly, is:
Clearly this function does not follow Elm's standard for argument ordering!
The History
I'm assuming this was originally implemented according to how
appendis implemented in Haskell: the contents of second argument are appended to the contents of the first. It's probably been this way since day one of Elm, back before standardized argument ordering or(|>)existed.The trouble is that now we have a strong convention in Elm that says if you write the code in the original sample above, you should reasonably expect to get
[ "foo", "bar", "baz" ].One might argue "yeah but then
List.append [ "foo", "bar" ] [ "baz" ]gets you[ "baz", "foo", "bar" ]which is surprising in a different way. This is true, but in practice nobody ever fully appliesList.appendexcept in examples. We write[ "foo", "bar" ] ++ [ "baz" ]instead.The place where you want to call
List.appendin actual Elm code is in a pipeline, but I never ever ever write|> List.append foobecause that code will do the opposite of what it says it's going to do. (I always write|> (++) fooinstead, because it's not self-documenting at all - which unfortunately is an improvement over misleading.)Proposed Fix
I hope it's not controversial to conclude that
List.appendshouldn't behave in a surprising way under the circumstance in which people will most often reach for it.The naive fix for this would be to flip it. Unfortunately this would be a disastrously backwards-incompatible change, as its type signature is
List a -> List a -> List a- meaning everyone's code will still compile for the flipped version, it will just do something completely different. This does not seem like a viable choice for a core function.A better fix, in my opinion, is to rename it (and any other core
appendfunctions implemented this way) toprependappendTo, per @rgrempel's suggestion. Here's how that would look:If that change happens, then:
List.appendfunction is replaced with an identically-implementedList.appendTo, which follows standardized Elm argument order and works as expected in a pipeline.Liststill exposes a function that makes it Monoidal, in case that ever matters.Some time in the future, enough major versions of(This made sense withcorewill have been released (presumably with auto-migrations in between) that aList.appendcan be safely introduced again, this time with Elm-style argument order.List.prependbut would not be necessary withList.appendTo.)