The violation of Object usage


#1

Hi all
I request the data to the server as following:

Axios.getWithConfig(url, Axios.config(~headers, ()));
  })
  |> Js.Promise.then_((resp: Currencies.t) => { 

Js.Promise.resolve(Js.log(Currencies.status(resp)))
  });  

As response, I force it to be the type Currencies.t that has following definition:

module Currencies = {
  [@bs.deriving abstract]
  type t = {
    status: int,
    data: array(string)
  }
};

It works fine, but I do not know, if I violate the language. It exists the lib https://github.com/glennsl/bs-json to convert JSON to reasonml but the example above works fine too.

Thanks


#2

looks fine to me! @bs.deriving abstract is perfectly valid way to interact with javascript objects.


#3

Yes it works fine but do I violate the semantic of the language?


#4

Type hacking is sometimes necessary. Just make sure you don’t abuse it


#5

I was porting some Javascript to Reason just today and had to use [bs.deriving abstract]. It was for a Sketch (vector design tool) plugin; it collects information about the document and sends to my server as JSON. I don’t do much processing on the collected data - it is transmitted immediately to the server. I ended up using [@bs.deriving abstract] here so that I can avoid writing encoders and instead rely on JSON.stringify. stringify can convert it into neat JSON, provided I stick to arrays and primitives, and not use records, lists, or variants.

But for other data I received from APIs I used JSON.decode to explicitly decode them to native Reason types. This is so that I can write more idiomatic functions around those data. deriving bs.abstract forces us to use getters and setters (idGet and setId for id column), and construct the structure using auto-generated functions. None of this is an issue in a type safety sense - everything type checks nicely, to me it is just an ergonomics thing.


#6

@jasim I’d only use [@bs.deriving abstract] if the object’s shape is very simple.

Obviously, since we’re telling the compiler to trust us about the shape of the object, run-time errors can be quite confusing. The following structure:

[@bs.deriving abstract]
type test = {
  data: string,
};

#...

returnsTest() |> data |> String.length |> Js.log;

could easily result in:

Uncaught TypeError: Cannot read property 'length' of null

And this is pretty mild. What’s worse is when there’s no error, and stuff seems to work. The above error wouldn’t be raised if data were an array at run-time, since it’ll respond to .length just fine.

However, bs-json truly type-checks:

Uncaught: [["Json_decode.DecodeError", ...], "Expected string, got null at field 'data"]

Seems to me that bs-json just saves a lot of time, at the end of day.


#7

True. As I said I’m using it only to encode to JSON and save a stringify. Which is also not much savings, given that I have to use keep in mind different record accessor APIs depending on whether it is native or deriving structures. For decoding, json decode all the way.