Compiler question: Why is valFromOption needed for abstract types?

type x
@val external x: x = "null"

let f = (~foo, ~baz, ~bar=x) => {
  [foo, baz, bar]
}


let g = (~foo, ~baz, ~bar="foo") => {
  [foo, baz, bar]
}


let h = (~foo, ~baz, ~bar=Some("foo")) => {
  [foo, baz, bar]
}

Outputs:

var Caml_option = require("./stdlib/caml_option.js");

function f(foo, baz, barOpt) {
  var bar = barOpt !== undefined ? Caml_option.valFromOption(barOpt) : null;
  return [
          foo,
          baz,
          bar
        ];
}

function g(foo, baz, barOpt) {
  var bar = barOpt !== undefined ? barOpt : "foo";
  return [
          foo,
          baz,
          bar
        ];
}

function h(foo, baz, barOpt) {
  var bar = barOpt !== undefined ? Caml_option.valFromOption(barOpt) : "foo";
  return [
          foo,
          baz,
          bar
        ];
}

I understand the reason why valFromOption is called for nested option<option<'a>>, but why is it called when manipulating abstract types like x here?

Another way of thinking about it is that valFromOption is only not needed if x is provably not a nested option.

So if type x is abstract, then the compiler has no way of knowing that option<x> isn’t option<option<'a>>, or option<'a>, or option<option<option<'a>>>, etc.

Yep, for correctness of being able to distinguish Some(None) from None.
It’s unfortunate but it’s needed. If you’d like to avoid it, then avoid polymorphic helpers for option like those.

Yep, for correctness of being able to distinguish Some(None) from None .

Yeah, as I said I get that part, I was just wondering the reason why it was necessary for abstract types (it doesn’t occur with primitive types, example 1 vs 2).

Is that because the abstract type can be an undefined literal that can be mistaken for a None? And if so, is there a way to annotate an abstract type so that we indicate that it cannot be undefined to avoid this overhead when possible?

The compiler has built-in knowledge about built-ins but not about abstract types.

I thought about such annotations, but it is an unsafe annotation since the compiler can not verify. The overhead maybe smaller than you thought, the added size is tiny while the runtime overhead seems quite small, can you elaborate a little bit about your concern.

For the size, we can still make it a bit smaller as below:

var bar = Caml_option.value(barOpt)

Is it worth it?

Maybe RescriptOption.encode & RescriptOption.decode would make it be a bit clearer?

1 Like