Switching on Js.Json.parseExn

I’m trying to convert Js.Json.parseExn into a Belt.Result I’ve tried switching, but I can’t seem to get any of the pattern matching for errors to work; it keeps thinking it’s getting a Js.Json.t back, but there is a lurking SyntaxError from JS. I’ve tried various try catch formatting, but can’t seem to get it.

I just want something like:

let saferParsingOfJSON = string =>
    switch Js.Json.parseExn(string) {
    | json => Belt.Result.Ok(json)
    | _ => Belt.Result.Error("cow")
    }

The above doesn’t work, and I don’t like the _; I’d love to get info on what type of error it is, and get access to the error message.

… er… wait, this works:

let saferParsingOfJSON = string =>
    switch Js.Json.parseExn(string) {
    | json => Belt.Result.Ok(json)
    | exception _ => Belt.Result.Error("cow")
    }

:: tries again ::

Holy crap, coffee is amazing.

let messageFromJavaScriptError = error =>
    switch error {
    | exception error => {
        let optError: option<Js.Exn.t> = Js.Exn.asJsExn(error)
        switch optError {
        | Some(error) =>
            switch Js.Exn.message(error) {
            | None => None
            | Some(errorMessage) => Some(errorMessage)
            }
        | None => None
        }
    }
    | _ => None
    }

let saferParsingOfJSON = string =>
    switch Js.Json.parseExn(string) {
    | json => Belt.Result.Ok(json)
    | exception err => Belt.Result.Error(Js.Option.getWithDefault("no clue", messageFromJavaScriptError(err)))
    }

mario-rave

Hrm… I still can’t quite get at the JavaScript specific exception. I’ve tried putting this in the middle of the messageFromJavaScriptError function above:

| Js.Exn.Error(obj) =>
        switch Js.Exn.message(obj) {
        | Some(m) => Some(m)
        | None => None
        }

But then other functions that pass in Js.Promise error’s don’t work with that switch.

Ok, I think I finally figured it out (for real this time). I changed the parsing to be:

let safeParse = string =>
  try {
        let json = Js.Json.parseExn(string)
        Belt.Result.Ok(json)
    } catch {
        | Js.Exn.Error(obj) =>
            switch Js.Exn.message(obj) {
            | Some(m) => Belt.Result.Error(m)
            | None => Belt.Result.Error("Unknown")
            }
    }

And that’ll return a result that has the JavaScript exception error string. Geez, I need some sleep.

Btw, Ok and Error are builtins, you can just do Ok(...) and Error(...). Belt.Result module just re-exports them.

2 Likes

Dude, that’s awesome, 2 follow ups now…

  1. Is there a list of these built ins? Like, where the heck is failWith documented, lol.
  2. How can I shorten 'em. Like instead of Js.Option.thing why can’t I go let Option = Js.Option? Is there a way to do something like that to remove the Belt or Js prefix to shorten things?

Those built-ins are not documented in one place, some of them live in different places of docs and some of them are not documented.

you can alias module names like this

module Option = Belt.Option

another way to get Option in scope is to open Belt

open Belt
let x = Some(2) -> Option.map(x => x*2) 

you can also bring some items in scope with pattern matching on packed module

let {map, keep} = module(Belt.Option)

3 Likes

Dude, #1 is exactly what I’ve needed all my ReScript Life®, thank you so much!

I don’t think they’re in the ReScript documentation (yet) but they’re inherited from OCaml. You can see them (in OCaml syntax) on those docs. ReScript is currently based on OCaml 4.06.

https://www.ocaml.org/releases/4.06/htmlman/libref/Pervasives.html

https://www.ocaml.org/releases/4.06/htmlman/core.html

Note that there are a few differences between those docs and what ReScript supports, but most of it should be the same.

1 Like

This can be shortened to:

let safeParse = string =>
	switch Js.Json.parseExn(string) {
      | json => Ok(json)
      | exception Js.Exn.Error(exn) =>
      	exn->Js.Exn.message->Belt.Option.getWithDefault("Unknown")->Error
}
4 Likes