For this code:
// val map : ('a -> 'b [@bs]) -> 'a option -> 'b option
let optTest: Js.Option.t(int) = Js.Option.map(x => x, Js.Option.some(1));
I am getting following error:
This expression should not be a function, the expected type is (. 'a) => 'b
where x => x
is red. I am really confused, why the map
isn't working? From its type signature it looks I am using it correctly, yet compiler says the first argument is not supposed to be a function?
Short answer - use Belt.Option.map
instead:
let optTest: option(int) = Belt.Option.map(Some(1), x => x);
Long answer:
The Js
namespace is intended mostly for bindings to JavaScript's standard APIs. And while Js.Option
for historical reasons was included in this namespace, the conventions used in the Js
namespace is still that of very thin bindings.
The type you see for the callback function in the documentation, 'a -> 'b [@bs]
, and the type you see in the error message, (. 'a) => 'b
, are exactly the same type. But the former is in OCaml's syntax while the latter is in Reason's, and also sugared up to look less offensive. Either way, the problem is that you're passing it an ordinary function when it expects this weird other kind of function.
The weird other kind of function is called an uncurried function. It's called that because "normal" functions in Reason are curried, while JavaScript functions are not. An uncurried function is therefore essentially just a native JavaScript function, which you sometimes need to deal with because you might receive one or need to pass one to a higher-order JavaScript function, like those in the Js
namespace.
So how do you create an uncurried function in Reason? Just add a .
, like in the type:
let optTest: option(int) = Js.Option.map((.x) => x, Some(1);
Or if you want to do it without the sugar (which you don't, but for the sake of completeness):
let optTest: option(int) = Js.Option.map([@bs] x => x, Some(1);
Addendum:
You might have noticed that I've replaced Js.Option.t
and Js.Option.some
in your example with option
and Some
. That's because those are the actual primitives. The option
type is essentially defined as
type option('a) =
| Some('a)
| None
and is available everywhere.
Js.Option.t('a)
(and Belt.Option.t('a)
) is just an alias. And Js.Option.some
is just a convenience function which doesn't have any equivalent in Belt.Option
. They're mostly just there for consistency, and you should normally use the actual type and variant constructors instead.