syntax
syntax copied to clipboard
Syntax sugar for open (a.k.a local open)
I'm receiving an error when locally opening a module that I'm not sure how to handle
4 │ Runtime.onInstalled(_ => {
5 │ DeclarativeContent.(
6 │ removeRules(_ => {
7 │ addRules([
8 │ {
I'm not sure what to parse here when looking at "(".
The error highlights removeRules
There's currently no syntax sugar for local open. We're looking into a solution. For now you can use:
Runtime.onInstalled(_ => {
open DeclarativeContent
})
I think it might be great that there is no open sugar, as it's can be confusing, and making local opens more explicit isn't a bad thing imo.
Ahh great, thanks @IwanKaramazow!
I would also be very happy if this would be possible:
let fn = () => Dom.{
...
}
@alexfedoseev Yes, that's actually a valid use case. I would like to look into alternatives.
Maybe we could take the old horrible JS equivalent-ish
with Cn ("one" + "two" + "three");
let fn = () => with Dom.Element {
...
}
I always found the local open syntax strange (never seen something like that syntax before) and hard to understand when learning Reason and reading code snippets. I think something like @bloodyowl suggestion reads much better for newcomers.
Local open adds parenses and indentation, overall quite a lot of noise in my opinion. I usually prefer to use open
but refmt transforms it back to a local open 😑
Maybe we could do something like
open Belt, Dom, Element
If we want a terse syntax
This really isn’t about terse vs. verbose open statements. It’s about being able to use multiple, mutually exclusive opens at the expression level vs. the scope level.
Local opens allow us to be much more explicit about precisely where each module is being used/opened.
The normal open
keyword will open the module for the entire enclosing scope, which is not always desirable. E.g. you might not be fully aware of which Array
module you are using, or the fact that your logging function is actually streaming data to cloud storage instead of to the console on your local machine.
The only other way to confine the scope of a normal open is to wrap your expression in a new block scope, and open the module there.
Obviously using local open can get out of hand, just like any kind of module opening, but a good style guide can go a long way in helping people avoid cryptic code. And it’s not that hard to learn the syntax for it.
@austindd it's unclear what direction local open should take at this point. Do you have more concrete example to illustrate your case?
So now ReScript formats a lot of one-liners in our codebase into at least 4-liners, e.g.
let clone = date => Js.Date.(date->getTime->fromFloat);
to
let clone = date => {
open Js.Date
date->getTime->fromFloat
}
Something terser would be very appreciated.
EDIT: Local opens are actually a very important feature, because compared to the import
s in JS, you only need to have some occasional open
s on the top of the file. Now they are all over the place in ReScript, adding more noise to a codebase than some parens would do.
I am running in this while converting our codebase. I realize we have quite a bit of local opens that don't translate very well. Such as:
<View style=Style.(style(~margin=dp(0.), ())) />
That becomes:
<View style={
open Style
style(~margin=dp(0.), ())
} />
@jfrolich after infix operators are in, we’ll look into the design and implementation of local open
Ooh that would be awesome!
If I can make one note for when that happens, please don’t automatically optimise single expressions to local open the way refmt
does. It’s really annoying 😅
I'm confused by the documentation. It says that local opens are possible:
Use open this sparingly, it's convenient, but makes it hard to know where some values come from. You should usually use open in a local scope:
let p = { open School getProfession(person1) }
But this thread suggests that it's not possible...?
@earksiinni
The docs refer to a "local scope" not "local open". When you wrap a block of code in braces, like { /* some code */ }
, you create a new scope. The School
module is only visible inside the area delimited by the braces. Does this help you a bit?
Ah understood. So local open is like a specialized form of local scope? Basically,
Cn.(...)
is equivalent to
{ open Cn; ... }
?
@earksiinni they're different syntax for the same thing. ClassNames.("one" + "two" + "three")
is visually just short one-liner vs the more heavy {open ClassNames; "one" + "two" + "three"}
(which usually written over multiple lines)
Another place I use this is for importing record fields:
let x = Module.{
a: 1,
b: 2
}
In theory I could just annotate the first key, but that looks more messy imo.
let x = {
Module.a: 1,
b: 2
}
let x = { Module.a: 1, b: 2 }
Annotating keys in record has always looked clumsy to me.
But for this case I guess you can just annotate x
.
Maybe? I admit I haven't tried that one, for a long time I ran with warning 40 enabled which requires record fields to always be in scope. I don't anymore though.
@tsnobip I think the Reason syntax also allowed this pattern:
let x = Module.{
a: 1,
b: 2,
}
This seems more natural and clear.
Another place I use this is for importing record fields:
let x = Module.{ a: 1, b: 2 }
This pattern (opening the module right before the record) yielded some shadowing in some cases of ours in the past.
In theory I could just annotate the first key, but that looks more messy imo.
let x = { Module.a: 1, b: 2 }
This is how we usually do it to avoid shadowing.
The difference being the latter one brings just the record fields into scope, where the former brings the whole module into scope.
Though I have to admit the second one doesn't strike me as beautifull.
Ah yes very true, I switch to annotated keys when shadowing is a problem.