Closed tfausak closed 8 years ago
For a more concrete example, how should 2 |> subtract <| 3
be parsed?
(2 |> subtract) <| 3
-- subtract 2 3
-- 1
2 |> (subtract <| 3)
-- subtract 3 2
-- -1
I think the former makes the most sense. It's the same as this:
2 `subtract` 3
-- subtract 2 3
-- 1
In fact, this is apparently a way to write functions as infix in F#. http://stackoverflow.com/questions/16548654/how-to-write-an-infix-function
Is there a compelling argument to be made for how <.
and .>
should mix? What should f .> g <. h
mean?
(f .> g) <. h
-- h .> f .> g
f .> (g <. h)
-- f .> h .> g
I think the former makes more sense, but only barely. "f and then g but first h" is ambiguous, but "(f and then g) but first h" makes the most sense to me.
After thinking about this for a while, I want to add this feature. It makes a lot of sense to be able to mix these operators.
Changed my mind after implementing this: https://github.com/tfausak/flow/pull/7#issuecomment-145395343
I understand that you've already made a decision on this, but anyways here's a different reason to maybe change the precedence of |>
to infixl 1.
I originally wrote something like the following:
x =
thing
>>= someAction
>>= anotherAction
& fmap something
>>= anotherAction
This associates like (((thing >>= someAction) >>= anotherAction) & fmap something) >>= anotherAction
.
If I replace &
with |>
, due to precedence rules (since |>
has precedence 0), it breaks. It associates it as:
x =
(thing
>>= someAction
>>= anotherAction)
|> (fmap something
>>= anotherAction)
which isn't what I intended (it doesn't even typecheck).
If we changed the precedence of |>
to 1, then my example would work. The best I can do, using the current precedences, is
x =
thing
|> (>>= someAction)
|> (>>= anotherAction)
|> fmap something
|> (>>= anotherAction)
which is too ugly in my opinion.
Further justification for mixing |>
and >>=
(as well as other "application-like" operators like <&>
): Since they're all "application-like" operators, it's quite clear that you're taking one thing and applying a sequence of operations on it. (Note that currently, &
, <&>
, and >>=
are infixl 1, and they play nicely together.)
As a side note, the base people chose to give &
precedence 1 and $
precedence 0 precisely so that you can mix &
and $
.
I'm personally not in favor of mixing |>
and <|
, but it might add some perspective that the base people were okay with it. Even if we think mixing |>
and <|
is bad behavior, maybe it's okay to allow mixing, but still discourage people from doing that. If they shoot themselves in the foot with it, that's their fault.
Anyways, I understand that you've already made a decision on this, and this might not be a good enough reason to change your mind.
Thanks for the comment and detailed motivation! I have to admit that when I decided about this I was only thinking about Flow, not the rest of the ecosystem. That being said, I personally prefer to avoid expressions like your example. I would much rather do this:
x = do
a <- someAction thing
b <- anotherAction a
let c = something b
anotherAction c
The fact that Flow doesn't play nice with your example is an accident. But I actually like the code it encourages you to write.
Flow isn't meant to be a drop-in replacement for anything. I know I have a cheat sheet in the documentation, but it's not meant to imply that |>
is exactly the same as &
. Otherwise what would be the point? If you want |>
to be infixl 1, you can get it from Data.Functor.Monadic.
Thanks for your response! In retrospect, I think your way of doing this is better in most cases.
In case you're curious, the situation that prompted me to write code like I mentioned above was when I was using xml-conduit (the XML-parsing library), which I don't think is very well-designed anyways. The library has a Cursor
type (which is defined as type Cursor = [XMLNode]
), and the operations for looking deeper inside the XML node all have type XMLNode -> [XMLNode]
(or, alternately, XMLNode -> Cursor
), so they encourage you to chain the operations using >>=
(by []
's Monad instance). This is pretty silly in my opinion; the solution here is to make a cleaner library interface, rather than using >>=
on list Monad instances as they suggest.
So in summary, I don't think this is a problem with Flow; someone should just make a better XML library interface.
xml-lens looks better. Thanks!
http://www.reddit.com/r/haskell/comments/324415/write_more_understandable_haskell_with_flow/cq8mehu
I think it's reasonable to treat
x |> f <| y
as(x |> f) <| y
.