GADTs and bs-socket.io


#1

I’m trying to integrate sockets into my ReasonReact app, but I’m getting tripped up with the GADT and locally abstract types.

I’m trying to implement a redux-like list of messages on the server, and reduce them to get the current state:

module Common = {
  type t('a) =
    | UpdateCard: t(Board.card)
    | ToggleRevealed: t(string)
    | UpdateBoard: t(Board.t);
  let stringify = (type a, t: t(a)) =>
    switch (t) {
    | UpdateCard => "UpdateCard"
    | ToggleRevealed => "ToggleRevealed"
    | UpdateBoard => "UpdateBoard"
    };
}

module ActionList = {
  open Belt;
  type action('a) = {
    action: Common.t('a),
    payload: 'a,
  };
  type t('a) =
    | List(action('a));
  let getNewState = (state, actionList) =>
    List.reduce(actionList, state, (acc, next) =>
      switch (next.action) {
      | UpdateCard => acc
      | ToggleRevealed => acc
      | _ => acc
      }
    );
};

I get this error in my getNewState function, and I don’t know what it means:

  76 ┆ List.reduce(actionList, state, (acc, next) =>
  77 ┆   switch (next.action) {
  78 ┆   | UpdateCard => acc
  79 ┆   | ToggleRevealed => acc
  80 ┆   | _ => acc

  The GADT constructor UpdateCard of type ReactTemplate.Common.t
  must be qualified in this pattern.

My best guess is that it has something to do with refining the types, but I’m not even sure what that means :man_shrugging:


#2

This error message has to do with the type being defined in Common module and used in another module.

You can qualify each constructor by the module name:

Common.UpdateCard => acc

or open the module inside ActionList:

module ActionList = {
  open Common;
  ...
}

But you will get into trouble with ToggleRevealed not being of type Common.t(Board.card).

you have to use a locally abstract type (as you did in stringify):

let getNewState = (state, actionList) =>
  List.reduce(actionList, state, (acc, type a, next:action(a)) =>
        switch next.action {
        | UpdateCard => acc
        | ToggleRevealed => acc
        | _ => acc
        }
  );

#3

Thanks, got it to compile!

Why do these types require qualification when a “normal” variant does not? Is this no longer a variant then?


#4

The type checker expects each branch of a match to be of the same type, which is not the case with GADTs, hence the locally abstract type trick.

These notes explain this better than I could: https://www.cl.cam.ac.uk/teaching/1617/L28/gadts.pdf

GADTs are significantly more complex than variants, so make sure you really need them!