Using Option.map to transform a non-option value

I’m trying to figure out if Rescript’s API already has a generic map. Currently I thought about using Option.map but seems to verbose for non-option values.

Is there any official way to do this? or should I just create my own utility map?

Updated: Playground with 5 options

Why did you choose mapWithDefault for example 1 and not Belt.Option.map?

A

good point, I tried it wit Option.map and works as well… a little less verbose :+1:

In your example, “Option 2” seems like the the most natural, easiest, and obvious choice. “Option 1” just seems bizarre, and both it and “Option 3” add unnecessary levels of indirection and make the code more complicated.

The Option.map function exists to, well, map option types. Just like Result.map, Array.map, etc each exist to map their respective types. If you want a map function for your custom type, then you’ll need to provide it yourself.

So I think the real question here is: what are you trying to solve? Your “Option 3” map function has this signature: ('a, 'a => 'b) => 'b which is really too generic to be useful. Again, it just adds a level of indirection around the 'a => 'b callback function. Why not just use the callback directly?

Instead of this:

let map = (x, mapper) => mapper(x)
let result3 = getQuantity()->map(qty =>
  switch itemType {
  | Piece => qty
  | Roll => qty *. 1000.
  }
)

Why not just this?

let map = qty =>
  switch itemType {
  | Piece => qty
  | Roll => qty *. 1000.
  }

let result3 = getQuantity()->map

(As an aside, this doesn’t fit the usual definition of a “map” function, but the naming isn’t as important here.)

Jorge you can also use partial application if you really want a specific mapper… it will be specific to the [functor] and maybe the internal types:

let mapper = (~itemType) => {
  Option.map(_, (qty) => {
     switch itemType {
    ...
    }
  })
}

@johnj @mouton
I think Belt.Float and Belt.Int should also have map methods such as Belt.Option and Belt.Result

so we could do

let result4 = getQuantity()->Belt.Float.map(qty =>
  switch itemType {
  | Piece => qty
  | Roll => qty *. 1000.
  }
)

In case of a custom type sounds fair to use a custom mapper.

Well I would like to write an inline function on the pipe instead of declaring a map function out of the pipe, just like the same fashion of methods like Array.map and Option.map.

Maybe there is a way to inline a function in a pipe that I don’t know of

Actually I could also use a tuple switch

let result5 = switch (getQuantity(), itemType) {
| (qty, Piece) => qty
| (qty, Roll) => qty *. 1000.
}

Whats the signature of that Float.map?

You can use parenthesis

let a = 2->(x => x * 2)->(x => x + 3)

If you really like the pattern you use for other data types like option result array etc but without a context (nullability, error, sequence etc) theres a data type called identity functor. You can define it as a variant with only one member holding value of type 'a. The main operation defined on identity functor is map.

3 Likes

I made it up, but I think it would be float => float

ooh this is very helpful, thanks!

1 Like