# Is it possible to help the optimizer?

I was playing a bit with code generation and I noticed an interesting behavior. Take the following simple code (partially stolen from this thread):

``````module Option = {
let flatMapU = (x, f) => {
switch x {
| Some(x) => f(. x)
| None => None
}
}
}

let reciprocal = (. x) => x == 0. ? None : Some(1. /. x)

let indirect = Option.flatMapU(Some(2.), reciprocal)
switch indirect {
| Some(x) => Js.Console.log(x)
| None => ()
}

let direct = switch Some(2.) {
| Some(x) => Some(reciprocal(. x))
| None => None
}
switch direct {
| Some(x) => Js.Console.log(x)
| None => ()
}

let immediate = Some(2.)
switch immediate {
| Some(x) => Js.Console.log(reciprocal(. x))
| None => ()
}
``````

Nothing particularly exiting, just applying `reciprocal` to a value if it’s `Some`. FYI: using the re-implementation of `Option.flatMapU` produces the same result as using `Belt`. Here the `esbuild` bundle:

``````(() => {
// node_modules/@rescript/std/lib/es6/caml_option.js
function some(x2) {
if (x2 === void 0) {
return {
BS_PRIVATE_NESTED_SOME_NONE: 0
};
} else if (x2 !== null && x2.BS_PRIVATE_NESTED_SOME_NONE !== void 0) {
return {
BS_PRIVATE_NESTED_SOME_NONE: x2.BS_PRIVATE_NESTED_SOME_NONE + 1 | 0
};
} else {
return x2;
}
}
function valFromOption(x2) {
if (!(x2 !== null && x2.BS_PRIVATE_NESTED_SOME_NONE !== void 0)) {
return x2;
}
var depth = x2.BS_PRIVATE_NESTED_SOME_NONE;
if (depth === 0) {
return;
} else {
return {
BS_PRIVATE_NESTED_SOME_NONE: depth - 1 | 0
};
}
}

// lib/es6/src/demo.js
function flatMapU(x2, f) {
if (x2 !== void 0) {
return f(valFromOption(x2));
}
}
var \$\$Option = {
flatMapU
};
function reciprocal(x2) {
if (x2 === 0) {
return;
} else {
return 1 / x2;
}
}
var indirect = flatMapU(2, reciprocal);
if (indirect !== void 0) {
console.log(indirect);
}
var x = 2;
var direct = x !== void 0 ? some(reciprocal(x)) : void 0;
if (direct !== void 0) {
console.log(valFromOption(direct));
}
var immediate = 2;
if (immediate !== void 0) {
console.log(reciprocal(immediate));
}
})();
``````

As you can see, the bundler is able to see through `valFromOption` in case of `immediate`. However, in the other two cases it’s unable to optimize anything. The `direct` case is pretty bad, because instead of removing `some`, the branch and `valFromOption`, we have all the overhead.

Said that, this is probably more a topic for the optimizer than the rescript compiler, but I am wondering: is there something that could be done to help the optimizer? I am asking because a nice rescript codebase would use `option` (and `result`) all over the place, and even if the major performance issue are related to everything else (i.e. DOM manipulation), it would be nice if some low-hanging fruits could be available to have better codegen.

hi, I would suggest you stop worrying about such things, this is just a small function shared by all.

With regard to optimizations, if the compiler know the option type as a concrete type, it will be specialized, so better code generated. take `direction` ,
`immediate` for example, its type is known when type checking (option). You can annotate the option type to a concrete type for micro-optimizations

bundler does not do such optimizations to my best knowledge.

Note it is weird that `\$\$Option` is not removed, maybe you can file an issue to esbuild repo

I am not “worried”: I strongly believe that the gain of a strong type system is really greater than the small overhead introduced in the code (which is probably optimized away from the JIT).
In any case, as human beings, we can reason (no pun intended ) about simple cases: if I see that for a micro-test that there are a set of operations that could be avoided, then it is a plausible opportunity for optimizations. If, for cases like this, nothing can be really done to improve, it is fine. But if the opportunity can be taken, then it’s right to do so, because at the very end very small performance gains sum up, and the resulting code could be significantly faster, even if by 1%.

I just realized that I messed up a thing without noticing (because `Console.log` takes virtually everything) – the right code for `direct` should be

``````let direct = switch Some(2.) {
| Some(x) => reciprocal(. x)
| None => None
}
switch direct {
| Some(x) => Js.Console.log(x)
| None => ()
}
``````

without the inner `Some`. In this way the optimizer is able to do the right thing, which is very nice!

At this point the only case that involves overhead is when helper functions are used (`flatMapU` in this case). From what you are saying (and what I found around), it looks like there isn’t a common way to tell bundlers “hey, this function is pure and very small, try to inline it”, right?

Good catch! However this is my fault: I did not create an empty `.resi` file to make `Option` private. Doing that makes `\$\$Option` disappear.