-
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
[feature wish] introduce real pipe syntax #
like elixir
#1452
Comments
What about overriding |
would have to make it protected and not overridable I guess. |
What about this: x
|> foo a
|> bar _ b
|> baz c To mean: (baz c (bar (foo a x) b)) |
so |
What is the reasoning behind this rewriting? |
The reason is that type inference flow from to right, having object as the first argument makes type inference, annotation, auto-completion works better |
@bassjacob I'm talking about using libraries that you don't control. It needs to be compatible with normal OCaml. |
@bobzhang How is that different after the rewrite? Is the compiler primitives behind |
@OvermindDL1 great point! (I'm not for or against this change) but I think you could get around that by only treating non-module code this way. Seems like a pretty invasive change, but probably doable. |
In addition to the fact that many many existing ocaml code, including in it's standard library, also assume piping to the end. |
Maybe thats the topic of a larger discussion... does the benefits of interop with ocaml libs outweigh the problems... |
Considering I am needing to compile for both the web (via bucklescript) and to native code (via the stock ocaml compiler), definitely yes. ^.^ |
Would u read my proposal again? It certainly would compile on both
backends. It would be a little awkward to consume ocaml libs when they do
some bad things like redefining pipe operator, but it is already the case
given different keywords
…On Thu, 5 Oct 2017 at 8:35 AM OvermindDL1 ***@***.***> wrote:
Considering I am needing to compile for both the web (via bucklescript)
and to native code (via the stock ocaml compiler), definitely yes. ^.^
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1452 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAtmK3GLrBX-5o4Q9EmKqS1sJU5iuX_Kks5spPctgaJpZM4PuWjU>
.
|
I still don't see the benefit the rewriting brings, even ignoring OCaml compatibility. |
As I said, this does not make compatibility worse with OCaml given that ReasonML already has different keyword sets. I would suggest to make ReasonML a subset of OCaml in the future, which means removing some flexibility from OCaml to make it more friendly for tools(including IDE). Other changes I have in mind: removing open, include, customized operators etc |
Removing features sometimes would make the language even better : ) |
This is a beautiful idea to solve |
@bobzhang Do you have some examples of the typing differences between As for the proposed removals, I'll let it at "please no, don't break the language, let's solve this socially" until they're formally proposed. |
@bobzhang Could you desugar f @@ x in the parsing level? |
tbh, you could not find an operator uglier than `@@`, that’s why I am saying we should use `<|` in the syntax
I could not do this on OCaml side, since it would change its semantics, but it is okay to do it in reason side, since
reason can _redefine_ its semantics
… On Oct 14, 2017, at 6:48 AM, YYC ***@***.***> wrote:
@bobzhang <https://github.com/bobzhang> Do you plan to desugar f @@ x in the parsing level?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub <#1452 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AAtmK1NpWKKWIxZ6P2_PpgwjZTX30GuQks5sr-jEgaJpZM4PuWjU>.
|
Thanks for your reply. |
Still thinking about this but regardless, Reason can create new syntactic forms and shortcuts and in doing so it would remain fully compatible with OCaml, and any OCaml backend. For example if |> is transformed at parse time into function application, we merely find another way to express the original |> which invokes a function named (|>). It's just a syntactic remapping of AST concepts. But I don't fully understand the benefits of doing it at parse time, as opposed to a later stage. I don't really see many downsides except that you couldn't redefine |> (not a huge downside in my opinion). What's the benefits though? |
@jordwalke bob's last sentence makes sense. Also #1511 |
"x |> f is not equivalent to f x, under my proposal it would be. Under current ocaml semantics, it works 95% of the cases, but it would cause surprises for the rest 5% " Bob, can you provide an example where it behaves unexpectedly? |
It maybe one example? |
|
@jordwalke are you onboard with |
I can't see anything wrong with doing so as long as we retain some way to also express the original form (losslessly converting from OCaml for example). Are there any other benefits or tradeoffs to be aware of besides better compiler output? @bobzhang could you comment about the 5% of the times that |
see different output for the let f ?(x=1) z ?(y=2) =
x + y + z
(* let v = f 3 *)
let v = 3 |> f |
Ah, that's a good example. The limitations of named argument when used as arguments to higher order functions like I think that's a good justification for applying To complete this feature, here is what would need to be done:
|
https://blog.janestreet.com/core-principles-uniformity-of-interface/ Here is my motivating example that why let f (xs: 'a list) (u : 'a -> bool) =
List.for_all u xs
let f2 (u : 'a -> bool) (xs: 'a list)=
List.for_all u xs
type t = { x : int ; y : int}
type u = { x : int ; y : int}
let t xs = f (xs : t list) (fun {x;y} -> x = y )
let t2 xs =
f2 (fun {x; y} -> x = y ) (xs : t list) (* would not compile *) |
Here's a Reason rewriting of Bob's example in Reason: let forAllItemsFunc = (xs: list('a), func: 'a => bool) => List.for_all(func, xs);
let forAllFuncItems = (func: 'a => bool, xs: list('a)) => List.for_all(func, xs);
type p1 = {x: int, y: int};
type p2 = {x: int, y: int};
let t = (items) => forAllItemsFunc(items: list(p1), ({x, y}) => x == y);
/* Does not compile - compiler expects items to be a list of p2 because
* it inferred the first argument to be of type p2=>bool */
let t2 = (items) => forAllFuncItems(({x, y}) => x == y, items: list(p1)); It shows a situation where the "t comes first" API design guideline mentioned by Jane Street prevents an unnecessary type error. Type inference infers the type of the first argument, then the second, and then tries to substitute them in the argument position of the function's inferred type. I think that normally this wouldn't put "t comes first" at an advantage/disadvantage but OCaml has something called "type directed record field disambiguation", which allows the type system to know if I think there are also cases where "t comes first" is put at a disadvantage for the same reasons as above (left-to-right bias of record field disambiguation). Here's an example where now "t comes first" won't compile but the other form will. let forAllItemsFunc = (xs: list('a), func: 'a => bool) => List.for_all(func, xs);
let forAllFuncItems = (func: 'a => bool, xs: list('a)) => List.for_all(func, xs);
type p1 = {x: int, y: int};
type p2 = {x: int, y: int};
/** Does not compile for the same reason, but this time to the disadvantage of
* "t comes first". */
let t = (items) => forAllItemsFunc([{x: 0, y: 0}], ({x, y}: p1) => x == y);
let t2 = (items) => forAllFuncItems(({x, y}: p1) => x == y, [{x: 0, y: 0}]) There are other reasons to favor "t comes first" for Reason - for example, JS developers are familiar with callbacks coming last. Iwan even built special printing support for it. There are some cases where "t comes last" is better - like when you have optional named arguments, the final unnamed Regardless, a good reason for |
Would I still occasionally get semi-surprised that this doesn't work: getThing() |> doThis |> doThat |> Some Perhaps because the syntactic similarity of function application and variant construction makes it seem like it should. |
@glennsl Yeah, that should be possible as well. It would come with some complexities. For example, would we pretend that variants like Would we pretend that |
Yeah, I'd say variants should not be treated as if they were curried. Mostly because the error message for a partially applied variant would likely be very confusing, but it also seems more consistent conceptually. |
@jordwalke very few people write type annotations for callback instead of the data |
|>
, <|
syntax instead of function#
like elixir
@bobzhang It sounds like Can we still keep thread-last? maybe the current syntax which is thread last can follow clojure's convention and be Removing the thread-last macro completely would mean that there's no way to pipe anything for a lot of APIs that are used to the default behavior for |
Yeah we will definitely keep thread-last. |
@jaredly I expect it to be used frequently, saving one character seems worthwhile. For the record, another reason is that avoid such ambiguity & more consistent interop with js libs
|
I'd probably vote against re-using |
My two cents: |
@Risto-Stevcev isn't |
Ah yeah, sorry had the wrong way around. |
One more argument in favor of “main parameter first”: it works better with naming, because the “label” for that parameter is the function name and it’s therefore better if that label is located directly before it. However, “positional last” is built deeply into the language. For example, you can’t erase the optional parameter
So that would have to be changed, too! |
It may make sense to collect all reasons in favor of this significant change in a single document (which doesn’t have to be long). To convince critics and in order to document it for the future. |
I made a comment which I would now disagree with, so I deleted it. I would put the jane street article in front, as to the reasons to make the change. I also very much do not like the |
#1804 landed |
for the record, rescript-lang/rescript#1671 is an example of cost of |
Does introduction of If not, am I understand correctly that the recommended pattern for practical usage is: open Belt;
myList
|> List.filter(_, foo)
|> List.map(_, qux)
|> List.reduce(_, acc, bar)
|> Option.flatMap(_, baz)
|> Option.getWithDefault(_, 42); |
You can use the same |
😮 Indeed! Never thought about it as saw no mentions in Re-specific changelogs and docs. Works excellent. Will it be kept or is it a highly-experimental feature intensive usage of which is discouraged for now? The very hot discussion in this issue interrupted suddenly and I’m not sure about the current official position/recommendation of RE authors. |
We'll keep it. So not highly experimental. Even if it was, we'd provide a good migration path just in case |
Introduce a syntax
#
(was|.
)which is equivalent to
the reason is that
x
's type is usually known, so that by type flow, a0, a1 can have better auto-completion and less type annotationEdit: maybe
|.
is too heavyweight, how about using..
would be translated to
Edit: I was convinced that
t
comes first seems to be better, it is easy to remember, and when we export functions to JS users, the API would be more familiar. Actually I think using#
seems betterThe text was updated successfully, but these errors were encountered: