Is this possible?
type test = {
attr1: option<string>,
attr2: string
}
// I would like to omit `attr1`
// but Rescript forces me to write `attr1: None`
let aaa: test = {
attr2: "I'm mr. Meeseeks. Look at me!"
}
Is this possible?
type test = {
attr1: option<string>,
attr2: string
}
// I would like to omit `attr1`
// but Rescript forces me to write `attr1: None`
let aaa: test = {
attr2: "I'm mr. Meeseeks. Look at me!"
}
type test
@obj external createTest: (~attr1: string=?, ~attr2: string, unit) => test = ""
let aaa = createTest(~attr2="I'm mr. Meeseeks. Look at me!",())
compiles to
var aaa = {
attr2: "I'm mr. Meeseeks. Look at me!"
};
Edit: Or with a more familiar structure
module Test = {
type t
@obj external make: (~attr1: string=?, ~attr2: string, unit) => test = ""
}
let aaa = Test.make(~attr2="I'm mr. Meeseeks. Look at me!", ())
That’s kinda what @deriving(abstract)
is doing as well, but it also introduces a lot more functionality than one would actually need:
module Test = {
@deriving(abstract)
type t = {
@optional attr1: string,
attr2: string
}
}
let aaa = Test.t(~attr2="I'm mr. Meeseeks. Look at me!",())
You can also write a constructor function for your type:
let make = (~attr1=?, ~attr2, ()) => {
attr1: ?attr1,
attr2: attr2,
};
let test = make(~attr2="", ());
The ?attr1
passes the argument as an optional value. The ()
is needed because the compiler requires a final positional argument to be able to understand when all named arguments have been passed in.
This will produce a value: {attr1: None, attr2: ""}
.
Ever since records as objects was released I’ve often pondered whether to request a way for records with optional fields to omit keys when the value is None
. Records are a much nicer way to write bindings than @deriving
and @obj
, those feel clunky now, but I’m forced to use them when undefined values must be omitted.
couldn’t this be done at a syntax level in rescript?
Do you have an example of “at the syntax level”?
I was thinking something similar to how @deriving(abstract)
works, rather than syntax, since omitting optional fields would need to happen at multiple levels of the compiler.
For example:
@omitOptions
type myRecord = {
attr1: int,
attr2: option<string>,
attr3: int
};
let value = {
attr1: 5,
attr2: None,
attr3: 6
};
This would then omit attr2
in JS:
var value = {
attr1: 5,
attr3: 6
};
value.attr2
will return undefined
whether or not the key was defined, so the runtime behaviour should be the same.
no sorry my bad, you would definitely need the type information too, not doable purely on a syntax level.
This was discussed in this issue:
I just realized we can do this with just a normal record type and an @obj external
:
type test = {
attr1: option<string>,
attr2: string,
}
@obj
external test: (~attr1: string=?, ~attr2: string, unit) => test = ""
let aaa = test(~attr2="I'm mr Meeseeks. Look at me!", ())
let () = switch aaa {
| { attr1: Some(a1), _ } => Js.log(a1)
| _ => ()
}
/* JS:
var aaa = {
attr2: "I'm mr Meeseeks. Look at me!"
};
var a1 = aaa.attr1;
if (a1 !== undefined) {
console.log(a1);
}
*/
Because of ReScript’s safe handling of undefined
as an option
type, this is even safe for pattern-matching or record field dereferencing. I’m surprised I didn’t think of it sooner.
EDIT: come to think of it, the @deriving(abstract)
PPX can probably be rewritten to do the above transformation and still be backwards-compatible while adding the new feature of making the type a normal record type.
Yes you can do that indeed, I’ve done it a few times, especially when bindings to types you both consume and produce, but it’s not type-safe though!
We might not need that with Hongbo’s proposal:
I hope it’ll land soon!
Can you expand on that? The limited usage I show in my example seems to be type-safe.
I mean you can change the type or the obj function and the compiler won’t warn you that the two are out of sync.
Oh yes, true. When you’re already dealing with externals though, it comes with the territory.
of course, but this adds one unsafe layer compared to regular bindings.
Am I wrong or is this now possible with the following syntax?
type test = {
one: string,
two?: string,
}
You can create the record without the key. The key implicitly has type option
. If you pattern match the key in the record you will get a warning but you can dereference the key without any warnings.
I couldn’t find any documentation for this. Can someone tell me what’s going on here?
You’re right. This is a feature which was introduced in Rescript v10.
Looks like the docs aren’t uptodate yet.