core icon indicating copy to clipboard operation
core copied to clipboard

Feature Request: Option::or_return_none()

Open gmlewis opened this issue 1 year ago • 2 comments

It seems like this README.md is out-of-date: https://github.com/moonbitlang/core/blob/34096f4e37269777274b3ed6a2eed499890ae030/option/README.md?plain=1#L45-L72

It says:

Ending expression with ? will automatically unwrap the Option, if the result is None, it will return early from the function.

AND YET... THIS WAS ONE OF MY FAVORITE MoonBit FEATURES that was eliminating so much ugly boilerplate code like this: https://github.com/gmlewis/moonbit-pdk/blob/40948f7d08128b4c36036276d9489f32be8e99db/examples/add/add.mbt#L7-L19 which reminds me of Java in a really bad way.

So I would like to make a feature request for Option::or_return_none() which would allow that same code to be written like this:

pub fn Add::from_json(value : @json.JsonValue) -> Add? {
  let value = value.as_object().or_return_none()
  let a = value.get("a").or_return_none().as_number()
  let b = value.get("b").or_return_none().as_number()
  ...
}

Unfortunately, this feature request may require compiler support, without which it might not be possible to write.

gmlewis avatar Aug 22 '24 13:08 gmlewis

You could try to use some monadic functions ( bind/flatten ) to alleviate this issue.

pub fn Add::from_json(value : @json.JsonValue) -> Add? {
  value
  .as_object()
  .bind(
    fn(value) {
      let a = value.get("a").map(Json::as_number).flatten()
      let b = value.get("b").map(Json::as_number).flatten()
      match (a, b) {
        (Some(a), Some(b)) => Some({ a: a.to_int(), b: b.to_int() })
        _ => None
      }
    },
  )
}

CAIMEOX avatar Aug 22 '24 14:08 CAIMEOX

You could try to some monadic functions ( bind/flatten ) to alleviate this issue.

Sorry, but that seems to me to be even harder to read than my current workaround.

gmlewis avatar Aug 22 '24 14:08 gmlewis

Why not use json pattern?

pub fn Add::from_json(value : Json) -> Add? {
  match value {
    { "a": Number(a), "b": Number(b) } => Some(...) 
    _ => None
  }
}

Yoorkin avatar Aug 23 '24 02:08 Yoorkin

Thank you, @Yoorkin , I will in this case.

But I would still like this feature request to be considered because there are myriad opportunities to take advantage of this in non-JSON-related code.

...Or simply bring back the ?. Seriously.

gmlewis avatar Aug 23 '24 11:08 gmlewis

hi @gmlewis we will add features like this which is more general and readable:

guard let Some (x) = expr else { return None }
guard let (Some (x), Some(y)) = (a,b) else { return None}
guard let pattern = expr else { raise (Failure("xx")} 

let v = a?.b?.c?.d // optional chaining

The ? was removed because the implicit non local control flow transfer generally makes code hard to reason about, we want to keep such things minimal

bobzhang avatar Aug 24 '24 04:08 bobzhang