-
Notifications
You must be signed in to change notification settings - Fork 428
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fast pipe sugar #1999
Fast pipe sugar #1999
Conversation
|
Also I feel like
feels better than
|
And how is this "adds support"? because we already have |
@jaredly I think |
One argument for |
@jaredly it wouldn't have spaces: Something
->transform
->another(thing) Like PHP/C++. This evokes OO-style fluent programming without compromises. Imagine under modular implicit: [1, 2]->toArray->get(0)->forceUnwrap->print All these functions come from different modules, but are somehow assembled together and this didn't require the list to inherit from anything. @hcarty BS disables |
hrmmmmm. my knee jerk reaction is to gag, but that's just my history with php resurfacing 😅 I guess I could get used to it. |
I prefer keeping |
For us, |
Hmmm. As the equivalent of obj
.f(x)
.g(y)
.h(z); in JS, I don't think obj
->f(x)
->g(y)
->h(z); feels any better than obj
|. f(x)
|. g(y)
|. h(z); I think |
1899203
to
96adee6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we just parse |.
into ->
for a brief period of time as a codemod?
@@ -0,0 +1,119 @@ | |||
/* tests adapted from https://github.com/BuckleScript/bucklescript/blob/master/jscomp/test/pipe_syntax.ml */ | |||
include ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's with the formatting here lol
} | ||
); | ||
|
||
let f = (a, b, c) => a ->(b, c); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove the space between a
and ->
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, on my todolist, still thinking this through, there are some challenges 🐒
|
||
let f7 = a => a ->(Some, Some, Some); | ||
|
||
/* fast pipe in combination with underscore sugar */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should the semantics of a->f(_, b)
be? x => f(a, x, b)
or x => f(x, a, b)
or f(a, b)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f(_, b)
desugars to (__x) => f(__x, b)
So a->f(_, b)
=== ((__x) => f(__x, b))(a)
src/reason-parser/reason_ast.ml
Outdated
open Ast_helper | ||
|
||
let parseFastPipe e1 e2 = | ||
let attr = (Location.mknoloc "reason.fast_pipe", PStr []) in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This annotation is probably not necessary right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is for pretty printing purposes I'm afraid.
a->f->g
is parsed as an ordinary g(f(a))
. We need the attribute to give refmt a hint that it actually wasn't g(f(a))
, but a->f->g
.
|
If we turn |
Also |
Let's keep Which also means you'd use |
I understand why it would work this way, but it's a little weird that About the tuple distribution:
I'm not sure about this one. Substitution of a value in an identifier produces completely different meaning:
Compared to In general, it's not yet clear how I think we should break this up into two pieces:
With Modular Implicits |
The tuple one is just to match BS' The order is important because of side-effects and how people might use it: The current PR already splits things into a transform and a ppx. We just bundle them together. Also, not too sure they should be overridable for now in userland (likewise |
This PR does transforms on a selective region of the AST inside of the parser (.mly). I think it would be better to do the pass on the entire AST at the end, where we do other passes, and where we can chain multiple ppx plugins together. @IwanKaramazow What's the role that |
The one about this PR I'm looking to improve is a cleaner separation of concerns so that |
Discussed offline. Let's just turn The situation we don't wanna end up in, is for a newcomer to have to learn both We still need to consider some of the stuff |
Clarification:
|
(If we're voting) I have some preference for |
Is the underscore syntax going to stick around? Would it be better to stick with only that sugar since it's more general and avoids the x
|> f(1.0, _)
|> Variant(_, "🐫")
|> g(_) |
@jordwalke the I'll address the feedback. |
I was writing a lengthy bikeshed comment on how |
-> parses into ocaml |. |. parses into ocaml |.
vote for: Ligature Request, ReasonML fast pipe: |. tonsky/FiraCode#613 |
Sorry for commenting so late, but what's the point of doing this in the first place? Why do we want to use use 2 operators for the same thing?
Secondly, when I see For example (using @jaredly's example):
Pretend you don't know what it does. What would you think it does? I would think it takes I would immediately then assume that Meanwhile, the Configuring |
I would not have any more of an idea than
I don't know if I would immediately assume private properties, but I do agree the empty parens is misleading. Yet, using
How so? |
Arrow operators have well known meanings. "Fast Piping" is definitely not one of them.
Yes it does. It does not nudge the user into false ideas and makes it evident that something more special than accessing a property is going on. I don't mind being confused, I do very much mind being mislead. Fundamentally though, what in the world is wrong with Once again, there is nothing wrong with As a number of people here have already stated, it also doesn't feel right. It doesn't give me the "ReasonML" vibe tbh. |
But what's the difference in practice? Either way you don't know what the code does, and the type system prevents you from using it wrong. In any case, upon further thought I think IMO ReasonML should consider hijacking |
The difference in a nutshell is that the process to figure out the functionality is different. People fallback to the familiar. It's getting kind of irrelevant to the discussion so whatever. Full agreement with second point. It is indeed a beautiful operator and as I said, should be reserved for something more appropriate to an arrow. I don't understand what you mean about hijacking |
I see a few points being made. As an outsider, I'll try to address them.
If the main reason is to split the compilation of these separate modules, why introduce new syntax? Or rather, how does introducing new syntax help you at this point? User ergonomics should play no role in a performance optimization of the tooling behind the language.
Is the intention to attract and appeal to a bigger audience by making the language look more like an imperative/oop counterpart? If that is the case, then I believe it should be crystal clear.
I could not agree with you more. One example, for me, is the I.e, in ruby and lisps,
While I am certainly awaiting ad-hoc polymorphism, for most people coming from Js or OOP, this is going to be outstandingly misleading. Overall I feel that this discussion could've been split into
I proposed before in Discord to introduce a lightweight RFC process so that impactful, long-term changes can be proposed, discussed, and eventually agreed upon or rejected under a clear, solid, community-driven basis. Like in the Rust community. That's the sort of attitude that would make me want to invest even more in Reason. |
examples: x.c->f->g === (x.c)->f->g x##c->f->g === (x##c)->f->g !a->f->g === !(a->f->g)
What's the difference between property/method access and function calling via piping? In many languages, there's this concept of the "implicit" In JS, when you define a method, you do so like: let obj = {
count: 100,
plusN: (n) => this.count + n
};
obj.plusN(20); But there's an implicit "this" being passed to every function, so it's actually more like this: let obj = {
count: 100,
plusN: (this, n) => this.count + n
};
obj.plusN(obj, 20); In my view, a non-OO language is not so different, it's just that we tend to maintain a separation of behavior (methods) from the data. let obj = {
count: 100
};
module Methods = {
let plusN = (this, n) => this.count + n
};
Methods.plusN(obj, 20); Now at this point, there really isn's much difference between the JS OO style, and this form, except the syntax is not really conveying the fact that we have some data and we are applying operations on it. But with the "fast pipe" it then becomes apparent: obj->Methods.plusN(20); In my view, this has many benefits over the classical OO patterns where methods are first class attached and inseparable from the object instance. It means you can treat any piece of data like it implements any method that happens to accept that object's type as the first argument. For example, if I have a list [1,2]->List.map(..)
[1,2]->YourLibrary.map(..) The data itself doesn't have to anticipate all the possible methods that might want to be called with it. So in my view, "fast pipe" really is not so different from method calls or "access". I believe that people love the typical OO patterns because they have been trained to think in terms of |
A large part of the concept of "this" is that "this" is the current object. You're describing the process of passing "this" between objects which defeats the entire point of it. Why can't we just let it be |. ? |
What is "the current object" but a parameter that is (in some languages) implicitly passed? In JS, the
You could even supply a different "current object" this way:
In Python, the
You may have a valid point, but I'm not seeing it at the moment. |
Haxe is a language that has a similar feature (where the https://haxe.org/manual/lf-static-extension.html I've found that it's a great feature. You can see that it also uses the convention of the left hand side of the "dot" becoming the first arg of the function. Haxe's approach is more type directed, but the concept is the same. Fast pipe is just more rudimentary in that it does not use type directed disambiguation (when modular implicits come, |
You folks sure are good at bikeshedding lol! We'd like to ask you for the benefit of the doubt (just like we've asked the same for the Reason 3 syntax transition, highly controversial but whose benefit has been obvious in retrospect). I'm not gonna rehash the pros and cons from both sides; see above for a nice, heated debate. There's one thing I'd like folks to remember though: if it doesn't work out, we have the infra in place to undo the change without too much churn (including auto formatting, as always). So we have a little bit more leeway to experiment here and a slightly bigger safety net. Yes, the churn of needing to learn an extra operator's still there, but that's the cost of such investments. We'll try to document this well at least. |
What are the metrics to decide if it "works out"? |
Controversial decision to remove spaces. |
This looks odd as well: foo
|> DataLast.bar
->DataFirst.bar
|> DataLast.baz
->DataFirst.baz
/* vs */
foo
|> DataLast.bar
-> DataFirst.bar
|> DataLast.baz
-> DataFirst.baz
|
You can think of
|
@IwanKaramazow Yeah, I got idea from the thread. I don’t understand why in this case spaces were removed but we still have them in case of |
Considering the intent behind changing foo
|> (DataLast.bar->DataFirst.bar)
|> (DataLast.baz->DataFirst.baz) |
Not that you need another dissenting voice but this feels like starting to jump the shark. I understand the OOP-like motive but it distances Reason further from JS. What's more is I'd rather just have classes that compiled sanely like Fable for the nicer autocomplete. If you really want it to be Pythonic why not just use the |
TLDR
->
refmt into->
|.
refmt into->
->
parses into ocaml|.
|.
parses into ocaml|.