-
Notifications
You must be signed in to change notification settings - Fork 62
add F#-style "pipe" operator |> #6615
Comments
As usually I'm skeptical about introducing new operators that don't exist or don't have the same syntax as in other languages (or do they?), because although their meaning has a justification, they're still cryptic for newcomers. I'm also skeptical about their value. I don't think I've ever wanted them. As a comparison I've wanted the I'm not opposed to them, just very skeptical, especially when I do believe I'm not opposed to those operators, but I'm not convinced at all yet. Just waiting to be convinced ;) |
I don't have an opinion about But I do think
If I'm not mistaken, the multi-argument use would look like: Integer? x = ...;
Integer? y = x ?|> ((x)=>times(x, 5)); |
@FroMage My feeling is that we would introduce I agree that
vs
|
I think request |> parseParameters |> validateParameters|> doStuff |> writeResponse vs Request request = ...;
value params = parseParameters(request);
value paramsAgain = validateParameters(params);
value output = doStuff(paramsAgain);
writeResponse(output); |
OK fine, true that's better, but again assumes single arguments is common.
OK but if you want to chain this more, it's really starting to smell, especially if you have a chain of heterogeneous number method arguments. |
Well that's the point of
But sure, this is optimized for the case of one argument, that's for sure. |
Until you start working with RxJava, Streams API, Spark, or any other functional API :) |
I can't sleep thinking about how powerful can this be, and how much I dislike the
Thoughts? |
Now that I think a bit more about this, I found the
Anyway, as we are proposing the syntax, we can also adapt the semantics to our needs.
|
I'd like to remind people that a compose like operator as @jvasileff would give 99% of the expressive power with minimal language changes - there are several operators you can't use on functions waiting for reuse. I also achieved a compose function that composes functions of a single type, and it's reverse case, quite trivially
If some type wizards could extend it to accept X(Y) this issue would boil down to an SDK call. But I doubt that's feasible, or only as
If I'm not mistaken, spreading would happen naturally. |
It would be more effective to have monadic for - then to have the pipe operator (which is only for single argument functions). If at all, it is necessary to have a right associative version of concatenation for the creation of immutable data structures. But I know that you will not accept this proposal, because it make a whole bunch of addition to ceylon necessary or at least desirable: Implicits, no constraint of e.g. Summable interface (chain different types), right associativity, ascii art (there is no justification against ascii art, e.g.why is there a fixed set of operators for any class??), operator precedence definition (I can really understand than you dont like that - it makes a language a horror) ... have fun - but you will not have, and for some reason it is good so. |
@simonthum the source of the discomfort with using |
Hi, @welopino
Well, that's not quite right: the proposal I've presented above is not just for single-parameter functions. Though, of course, it's most natural in that case.
No, that's not right at all. Ceylon already has higher-order generics, so the type
I don't see what "implicits" have to do with this at all. The problem of abstracting over the notion of "composition" at a high level is a job for higher-order generics, not for implicit type conversions.
What you're trying to describe here is the higher-order generic type
Well you went all ranty at the end here, and I can't tell what this has to do with the issue we're discussing. |
There has been some discussion between @someth2say, @luolong, and myself on the gitter channel. Both @simonthum and @someth2say have been pushing in the direction of a syntax for function composition, instead of the F#-style "piping" at the value level. It seems to me, however, that
I can't seem to be able to construct any case where this would be ambiguous and/or non-associative right now. Perhaps I'm being dense, and there's something obvious that I'm missing. |
Well, OK, there's this one:
That case is indeed ambiguous :-/ |
Well in fact, anything that
I can see several approaches for solving the ambiguity:
|
Well, yes, of course. And can you think of any other type than |
A couple other interesting examples: class X() satisfies X(X) {}
Float f(X g(X x)) => 1.0;
Float | Float(X) whatIsIt = X() |> f; (similar for and this, which is an extension of the Anything(String)(String) a => nothing;
String(Anything(String)) b => nothing;
String | String(String) whatIsIt = a |> b; |
Interesting. Second example:
So in this example, desugaring
So whatIsIt is an
Agree, I've been a bit a cheater here, creating the |
just gotta say I don't especially like |
Unicode characters are notoriously difficult to type... |
@xkr47 I like unicode too, but it seems time for exclusive non-ASCII tokens in a programming language has not yet arrived. However, non-ASCII tokens could be alternate forms of ASCII ones: for example, Haskell has a compiler extension (and a source code directive) for that. |
I actually proposed support for ∨ ∧ ∀ ∃ ∈ ≤ ≠ etc. a while ago ;) I wasn’t entirely joking, but sadly nothing came of it. |
Hrm, so I suppose it would be possible to get rid of the parens around anonymous functions, and write void fun()
=> "hello world"
|> String.size
|> (len) => Integer.format(len, 16)
|> print; If we parsed anonymous function bodies with a slightly higher precedence (higher than assignments and |
Well, actually, I've managed to do a bit better than that. I've hacked it so that That actually feels pretty reasonable to me.... |
This is now working well enough that you folks can try it out, if you like. For example, the following code: shared void run() {
"hello world how are you today, I'm doing great !!!! xxxxxyyy"
|> String.size
|> (Integer i) => Integer.format(i, 16)
|> String.uppercased
|> String.trimmed
|> print;
} prints |
So I'm still not completely finished with this. The following issues remain:
|
Yeah despite the cleaner look, it only feels natural when you split the |
And of course the normal-precedence version would be auto-indented like this: shared void run() {
"hello world how are you today, I'm doing great !!!! xxxxxyyy"
|> String.size
|> (Integer i) => Integer.format(i, 16)
|> String.uppercased
|> String.trimmed
|> print;
} .. revealing the precedence thought error |
ASSOCIATIVITY FTW!!!!11!1 |
- higher precedence for |> - commit to desugaring-based impl
Done. And I threw in an impl of #3229, the
Still TODO. |
I've now also added
|
Parameter type inference is done for |
not sure what associativity they should have
I've merged this work to master. Still need to mention |
Added to spec. Closing this issue, but please reopen if you run into any serious problem with this work. |
Ceylon used to have something like that, in the ancient past, where |
yeah.. interesting thread.. this would have aliased a bit differently but I guess same/similar problems would arise. |
Several times in the past, users (including me!) have requested an F#-style pipe operator, which accepts any type
T
has the left operand, and a unary function typeF(T)
as the right operand.would be an abbreviation for:
Thus, you could write
print("hello")
as"hello" |> print
.If I'm not mistaken,
|>
is naturally left-associative.I'm now favorably-disposed toward this proposal, and the syntax seems viable. It looks like it can be implemented by desugaring and need not impact the backends in the initial implementation.
An open question is: is
foo |> bar
considered a legal statement? Can I write this:I would say that we should accept this code.
Discussion on Gitter led to me proposing two additional variations of this operator, by analogy to the existing
?.
and*.
operators.maybe ?|> fun
would propagate nulls from left to right, being equivalent toif (exists _=val) then fun(_) else null
tuple *|> fun
would spread a tuple over the parameters of an arbitrary-arity function, being equivalent tolet (_=val) fun(*_)
(Note that these operators could also be easily defined in terms of
|>
and a higher-order function. For example,tuple *|> fun
is equivalent totuple |> unflatten(fun)
.)Both of these are useful—I've wanted something like
?|>
many times when writing real Ceylon code—but the objection was raised by @someth2say that they're too ascii-arty for this language. That's a reasonable objection, and we should give it some weight.Feedback?
The text was updated successfully, but these errors were encountered: