Skip to content

Idea for decoder type #40

@andywhite37

Description

@andywhite37

This is purely for discussion/passing consideration, but I was poking around with the code, and I noticed it might be interesting to define a type for the decoder function Js.Json.t => M.t('a)

One way to do it would be like this (the type annotations were for me - you can leave them out):

  // inside DecodeBase
  type t('a) =
    | Decoder(Js.Json.t => M.t('a));

  // run function to apply the json input and produce the M result
  let run: (Js.Json.t, t('a)) => M.t('a) =
    (json, Decoder(decode)) => decode(json);

  module Functor: FUNCTOR with type t('a) = t('a) = {
    type nonrec t('a) = t('a);
    let map = (f, Decoder(decode)) => Decoder(decode >> M.map(f));
  };

  module Apply: APPLY with type t('a) = t('a) = {
    include Functor;
    let apply: (t('a => 'b), t('a)) => t('b) =
      (Decoder(f), Decoder(decode)) =>
        Decoder(json => M.apply(f(json), decode(json)));
  };

  module Applicative: APPLICATIVE with type t('a) = t('a) = {
    include Apply;
    let pure = a => Decoder(_ => M.pure(a));
  };

  module Monad: MONAD with type t('a) = t('a) = {
    include Applicative;
    let flat_map: (t('a), 'a => t('b)) => t('b) =
      (Decoder(decode), f) =>
        Decoder(json => M.flat_map(decode(json), a => f(a) |> run(json)));
  };
  
  // ... etc

Another way would be to leave out the data constructor Decoder and just make it an alias. I don't think you'd have to change much at all if you just did this.

type t('a) = Js.Json.t => M.t('a);

Doing that kind of cleans up the signatures for map/apply/bind/etc, and makes the implementations maybe a little more clear in that the Js.Json.t => M.t('a) bit would be hidden inside a type, rather than repeated. It might also make it more obvious what your core decoder type is for those new to the library - at its core it's just a Js.Json.t => M.t('a) function.

I'd also point out that the decoder function 'a => M.t('b) is basically the same as the definition of ReaderT. https://github.com/reazen/relude/blob/master/src/Relude_ReaderT.re#L10 - there might be some things to reuse from Relude or some general ideas that might come out of that. ReaderT is an abstraction where you can compose functions that will eventually be supplied some "environment" value in order to run and produce the result. In this case the "environment" is the json value.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions