reasonml-community / belt

MIT License
49 stars 1 forks source link

result, option #7

Open chenglou opened 6 years ago

chenglou commented 6 years ago

People keep asking where they are, and we point them to Js.Result and Js.Option. Since we might be opened by default in the future we can just polyfill these. This lib will serve as future ocaml stdlib polyfills too.

This duplicates what BS has, but we should remove these two from Js.* anyway

arnarthor commented 6 years ago

Expanding on option here.

This is what we have for options internally and I find these utility functions really useful

let get =
  fun
  | Some(v) => v
  | None => assert false;

let fold = (f, default, opt) =>
  switch opt {
  | Some(v) => f(v)
  | None => default
  };

let map = (f, opt) =>
  switch opt {
  | Some(v) => Some(f(v))
  | None => None
  };

let flatMap = (f, opt) =>
  switch opt {
  | Some(v) => f(v)
  | None => None
  };

let getOrElse = (default, opt) =>
  switch opt {
  | Some(v) => v
  | None => default
  };

let exists =
  fun
  | Some(_) => true
  | None => false;

let empty =
  fun
  | Some(_) => false
  | None => true;
jaredly commented 6 years ago

Maybe s/get/force/ or forceUnwrap? Or something to make it clear you're doing something exceptional. Also fold is a term I'm going to have to get used to 😅 I wonder if there's a descriptive name that doesn't rely on FP familiarity. And maybe isEmpty given that empty might be either a verb or an adjective? (with isPresent as the opposite? although exists isn't too bad)

chenglou commented 6 years ago

Yeah I'm not too sure about map/flatMap/fold yet. But we could always use them as aliases for more common names, for educational purposes.

jonlaing commented 6 years ago

Re: fold, I'm just using default which will return the value or a fallback:

let default = (d, o) =>
  switch o {
  | None => d
  | Some(a) => a
  };

So you can just pipe along:

myOpt |> default(3) |> f;
/* or */
myOpt <$> f |> default(3);

https://github.com/jonlaing/rationale/blob/master/src/Option.re#L17

TheSpyder commented 6 years ago

I don't want exception throwing methods in our stdlib at all. The ocaml stdlib has enough of those, and it will encourage accidents.

I use the name bind instead of flatMap and also aliased it to (>>=) because that's the monadic name ;)

Here are some of my helper functions as I mentioned in discord. I tend to avoid the use of fold these days, while technically correct I find people confuse it with foldl. The name I use in my JS FP lib is cata (since it's a catamorphism) but tbh I found lazy values really good for this when using option types so that's what I've done.

Type list:

let catOptions: list(option('a)) => list('a);
let value: (~default: 'a, option('a)) => 'a;
let orLazy: (Lazy.t('a), option('a)) => 'a;
let selfOr: (Lazy.t(option('a)), option('a)) => option('a);
let filter: (~f: 'a => bool, option('a)) => option('a);

Implementations:


let rec catOptions =
  fun
  | [] => []
  | [Some(x), ...xs] => [x, ...catOptions(xs)]
  | [None, ...xs] => catOptions(xs);

let value = (~default) =>
  fun
  | Some(s) => s
  | None => default;

let orLazy = (v) =>
  fun
  | Some(o) => o
  | None => Lazy.force(v);

let selfOr = (default) =>
  fun
  | Some(_) as o => o
  | None => Lazy.force(default);

let filter = (~f, opt) =>
  switch opt {
  | Some(s) when f(s) => opt
  | _ => None
  };
seanpoulter commented 5 years ago

Is there a high-level doc that explains where to find the "standard" OCaml libraries @chenglou?

I'm just getting into Reason and have an F# bias after reading Domain Modelling Made Functional. It has been very confusing finding Option.map. Here's what I'd found in the little bit of spare time I had this weekend:

It's a bit silly, so ... how can I help? I'd love to help with the docs / blogs / issues. My initial experience has been this is WAY more painful than it needs to be. It was so simple to set up but unclear how to get past Option.map. F# was awkward to set up but "Just Worked™". 🤔

TheSpyder commented 5 years ago

The reversed parameter order has been discussed ad nauseam, there are people on both sides of the fence in the community but it doesn't seem likely to change. As far as I know belt is intended to be used instead of the "standard" ocaml implementation, and certainly it's the one that will be the focus moving forward.