rescript-core icon indicating copy to clipboard operation
rescript-core copied to clipboard

fill out the Option module?

Open jmagaram opened this issue 1 year ago • 8 comments

I like using Option. I'm wondering if the module should be filled out with some functions that are available in Option packages from other languages. Most of these functions are simple conveniences that aren't strictly necessary. But they lead to shorter and sometimes easier-to-understand code. If we provide conversion functions from both sides, like option->null and null->option, the programmer finds it more easily. Maybe standardize on terminology for lazy forms. These usually take just a few lines to implement. What do you think? Should we consider augmenting the Option module? Similar changes could be made to Result as well. I've looked at F# and fp-ts. Here are some ideas. If there is interest in this, I can do more research and make a proposal.

module type Ideas = {
  let valueExn: option<'a> => 'a // same as getExn
  let valueUnsafe: option<'a> => 'a
  let valueOrDefault: (option<'a>, 'a) => 'a
  let valueOrDefaultWith: (option<'a>, unit => 'a) => 'a // new lazy form
  let concat: (option<'a>, option<'a>, ('a, 'a) => 'a) => option<'a>
  let orElseWith: (option<'a>, unit => option<'a>) => option<'a> // lazy form
  let map2: (option<'a>, option<'b>, ('a, 'b) => 'c) => option<'c>
  let map3: (option<'a>, option<'b>, option<'c>, ('a, 'b, 'c) => 'd) => option<'d>
  let fold: (option<'a>, 'b, ('a, 'b) => 'b) => 'b
  let match: (option<'a>, 'b, 'a => 'b) => 'b // same as mapWithDefault?
  let matchWith: (option<'a>, unit => 'b, 'a => 'b) => 'b
  let iter: (option<'a>, 'a => unit) => unit
  let exists: (option<'a>, 'a) => bool
  let contains: (option<'a>, 'a) => bool
  let count: option<'a> => int
  let flatten: option<option<'a>> => option<'a>
  let toArray: option<'a> => array<'a>
  let toList: option<'a> => list<'a>
  let toUndefined: option<'a> => Js.undefined<'a>
  let toNull: option<'a> => Js.null<'a>
  let fromUndefined: Js.undefined<'a> => option<'a>
  let fromNull: Js.null<'a> => option<'a>
  let fromPredicate: ('a, 'a => bool) => option<'a>
}

module Option = {
  let fromPredicate = (a, pred) => a->Some->Option.filter(pred)

  let concat = (a, b, f) => {
    switch (a, b) {
    | (a, None) => a
    | (None, b) => b
    | (Some(a), Some(b)) => Some(f(a, b))
    }
  }

  let fold = (i, j, f) => {
    switch i {
    | None => j
    | Some(i) => f(i, j)
    }
  }
}

module Examples = {
  let nums = [Some(5), None, Some(9)]

  let sum1 = nums->Array.reduce(0, (sum, next) => next->Option.fold(sum, (i, j) => i + j))
  let sum2 = nums->Array.reduce(None, (sum, next) => sum->Option.concat(next, (i, j) => i + j))

  let s2 = (s: string) =>
    s
    ->String.trim
    ->String.concat("abc")
    ->String.replace("a", "x")
    ->Option.fromPredicate(i => String.length(i) <= 50)
}

jmagaram avatar Mar 03 '23 23:03 jmagaram