nkrkv / jzon

ReScript library to encode and decode JSON data with type safety.
https://nkrkv.github.io/jzon/
Other
76 stars 4 forks source link

refactor: some nesting improvement #16

Closed An-Tu closed 2 years ago

An-Tu commented 2 years ago

HI! I don't know and maybe it doesn't a problem but this PR is just some trying to change this pattern

switch ...
  switch ...
     switch...
       and so on

Perhaps it will be of interest to you =)

nkrkv commented 2 years ago

Hello, thanks for the PR!

The reason this boilerplate exists is performance. The current (ugly) version compiles to a flat sequence of “if / return / if / return / if / return/ if / return / else” in JavaScript. So, no calls, no nesting.

I haven’t tried but suspect your version is going to be heavier, not sure.

In any case, the boilerplate is auto-generated with templates (https://github.com/nkrkv/jzon/tree/master/templates) via make objectn.res and make objectn.resi

An-Tu commented 2 years ago

Yes, I understand what you mean.

In the new flat version, we will get Result.flatMap and Result.map calls in JS.

We also can implement the ap function like this

@inline
let ap = (fn, r) =>
  switch (fn, r) {
  | (Ok(fn), Ok(r)) => Ok(fn(r))
  | (Error(_) as err, _)
  | (_, Error(_) as err) => err
  }

Then, in JS we get (it is part of the object4 function as example)

          decode: (function (json) {
              return Belt_Result.flatMap(asObject(json), (function (fieldset) {
                            var r = decode$1(field4, fieldset);
                            var r$1 = decode$1(field3, fieldset);
                            var r$2 = decode$1(field2, fieldset);
                            var fn = Belt_Result.map(decode$1(field1, fieldset), (function (val1, val2, val3, val4) {
                                    return Curry._1(construct, [
                                                val1,
                                                val2,
                                                val3,
                                                val4
                                              ]);
                                  }));
                            var fn$1;
                            fn$1 = fn.TAG === /* Ok */0 ? (
                                r$2.TAG === /* Ok */0 ? ({
                                      TAG: /* Ok */0,
                                      _0: Curry._1(fn._0, r$2._0)
                                    }) : r$2
                              ) : fn;
                            var fn$2;
                            fn$2 = fn$1.TAG === /* Ok */0 ? (
                                r$1.TAG === /* Ok */0 ? ({
                                      TAG: /* Ok */0,
                                      _0: Curry._1(fn$1._0, r$1._0)
                                    }) : r$1
                              ) : fn$1;
                            var r$3;
                            r$3 = fn$2.TAG === /* Ok */0 ? (
                                r.TAG === /* Ok */0 ? ({
                                      TAG: /* Ok */0,
                                      _0: Curry._1(fn$2._0, r._0)
                                    }) : r
                              ) : fn$2;
                            if (r$3.TAG === /* Ok */0) {
                              return r$3._0;
                            } else {
                              return r$3;
                            }
                          }));
            })

Apparently, the old version is more productive, the only question is by how much.

In any case, I did not pay attention to the fact that there is auto-generation of code, so my PR is not relevant.

nkrkv commented 2 years ago

the only question is by how much

Yes, that’s the question :) I don’t have enough energy to set up an experiment. So, for now, I’d leave everything as is, albeit your version is much more clear. Feel free to reopen if you find they are approx equivalent in performance.