Skip to content
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

v2 syntax still used in lazy pattern matching #2575

Closed
TheSpyder opened this issue May 15, 2020 · 12 comments · Fixed by #2774
Closed

v2 syntax still used in lazy pattern matching #2575

TheSpyder opened this issue May 15, 2020 · 12 comments · Fixed by #2774

Comments

@TheSpyder
Copy link

I have come across a case where refmt not only supports v2 syntax but outputs it 😮

While reading the BuckleScript blog post about the new lazy encoding, I noticed a cool way to unpack lazy values without calling Lazy.force:

let (lazy(value)) = myLazyValue;

Or rather, that's what I expected the syntax to be. Turns out the syntax in the blog post is not only valid reason, it's the syntax refmt converts the above to. No brackets.

let lazy value = myLazyValue;

proof this syntax works:
https://reasonml.github.io/en/try?rrjsx=true&reason=DYUwLgBAtgngMgQwF4wGoOAVxBAvBYZGACgCIALAS1IEoBuAKAYHoAqCAd3IUgEkIQADwAOIAMaRWzBqEjFCKYgDcM2GjTzR4RdFhCMAUgGcAdMAD2Ac2WqQ9Jm07dIAJxAAzKJHOYww30YQUjLgBEQQKnqasIgoutiGphbWkWqMQA

I'm using

$ ./node_modules/.bin/bsrefmt --version
Reason 3.6.0 @ 8f71db0
@TheSpyder
Copy link
Author

Counter argument: perhaps defining a lazy should be a changed to bracket-less syntax to match this pattern. As discussed in discord last night, it's confusing:
https://discord.com/channels/235176658175262720/235199119747055616/711938366194843788

Rather than lazy(myFunction(something)) it would be nice to have lazy myFunction(something). I think this would solve the ambiguity compared to otherFunction(myFunction(something)); the brackets here for lazy() make it less friendly imo, not more.

@yawaramin
Copy link
Contributor

Parentheses were added for constructing lazy values in #2376. However I guess we missed adding them to the lazy(...) pattern as well at the time (I didn't know it could be a pattern at the time). I think for overall consistency it's better to add it to the pattern e.g. let lazy(x) = lazy(1);.

@TheSpyder
Copy link
Author

I didn't know about it either until the blog post 😂

I agree we should be consistent, but after the discord discussion I think I'm leaning towards reverting to never have brackets rather than adding them to the pattern matching.

@yawaramin
Copy link
Contributor

We should consider overall syntax consistency. Reason syntax makes any kind of value construction look like function application e.g. let x = Some(0);. And conversely so does destructuring e.g. let Some(y) = x;. This simplifies the syntax overall–you don't need to remember a special rule for constructing and destructuring lazy values.

@TheSpyder
Copy link
Author

I think special syntax for special things makes sense, for example in JS await doesn't use function syntax. If reasonml ever adds that directly (instead of via let.await) it will definitely be expected to not use function syntax.

For constructing values, sure, function syntax makes sense. But lazy doesn't construct a value, it suspends constructing a value.

@yawaramin
Copy link
Contributor

yawaramin commented May 19, 2020

Reason has already added let-operators. It's part of the language now. [Edit: I think I misunderstood you, do you mean if Reason adds await syntax after already adding let-operators? That's possible I suppose but I don't think it's worth considering too much.]

lazy constructs a value of type Lazy.t.

@TheSpyder
Copy link
Author

Yes, I meant adding await even though we have let-operators. let.await isn't the same thing; it requires a library to implement it.

Lazy.t is only a value in the strictest sense of the word. As far as developers using it are concerned, it's about as much of a value as Promise.t is. lazy(1+2) doesn't produce 3 until you force the computation.

@yawaramin
Copy link
Contributor

But the whole point of promises is to turn asynchronous computations into first-class values that can be passed around. Lazy values are values in exactly the same sense. Anyway I think we are really in #bikeshedding territory now.

@TheSpyder
Copy link
Author

We are definitely bikeshedding, but if I do nothing brackets will be added. I'm growing more convinced reverting brackets is the better answer.

But the whole point of promises is to turn asynchronous computations into first-class values that can be passed around. Lazy values are values in exactly the same sense.

Yes, they are both special values that suspend computation from the line of code they're defined in, which is why having special syntax for both seems fine to me. let.await is a form of special syntax, so is await if that later becomes important enough to include, but I can't define let.lazy.

@yawaramin
Copy link
Contributor

await is not constructing a promise, it's binding to an existing promise and letting a continuation run on it. It's not the same as lazy(...). In fact JavaScript promises don't even suspend computation, they run eagerly. So the semantics of await really are quite different. In the hypothetical low probability of await being added to Reason, it's perfectly sensible for it to look like JS await.

@TheSpyder
Copy link
Author

Promises suspend computation at the line of code they are defined on, yes they run after the current stack frame but that's an implementation detail.

I don't actually use async/await so I always get them confused. I guess async is the one I meant then? My point was that JS programmers are already used to special syntax that changes how the line of code operates, whether that's binding to the promise result or creating the promise to begin with. We should be fine with it too.

@yawaramin
Copy link
Contributor

yawaramin commented May 19, 2020

We've spent quite a bit of time talking about a hypothetical async/await syntax for Reason, something I'm fairly sure will not happen now that let-operators are officially part of the language. But just to be clear. There's no special syntax in JS for creating promises, unless you count marking a function as async which lifts its return value into a promise. But this is syntactically separate from the return statement itself.

As I mentioned before, we already have a syntax for constructing values in Reason, and it looks like function application. Yes, constructing lazy values is semantically different, but it's not different enough that it warrants some special rule. Consider that in OCaml syntax, it looks like normal function application too: let x = lazy 1. I don't see any justification for Reason to be different.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants