How to define the key type for Js.Dict.t?


#1

I’m trying to write bindings for xstate, and it expects the following data structure:

const lightMachine = Machine({
  key: 'light',
  initial: 'green',
  states: {
    green: {
      on: {
        TIMER: 'yellow',
      }
    },
    yellow: {
      on: {
        TIMER: 'red',
      }
    },
    red: {
      on: {
        TIMER: 'green',
      }
    }
  }
});

The keys in the states dict, and the values of the on dict must be values of the same “set”. I tried modeling this set as a variant, and using Js.Dict.t for on, and states with the following code:

type machine;

type gameState =
    | PickFromSpot
    | PlaceToHolding
;


[@bs.deriving abstract]
type state = {
    name: gameState,
    on: Js.Dict.t(gameState)
};

[@bs.deriving abstract]
type machineSettings = {
    key: string,
    initial: gameState,
    parallel: bool,
    states: Js.Dict.t(state)
};

[@bs.module "xstate"] external machine: machineSettings => machine = "Machine";

let state1 = state(
    ~name=PickFromSpot,
    ~on=Js.Dict.empty()
);

let states = Js.Dict.fromList([
    ("state1", state1) /* This is the messed up part */
]);

let machine = machine(machineSettings(
    ~key="hello",
    ~initial=PickFromSpot,
    ~parallel=false,
    ~states
));

link to playground

It does not work, since on line 31 I would need to refer to a gameState, which I can’t.

I feel like the solution would be to use some kind of Map<gameState, state>, but I can’t find a way to do that with Js.Dict.t.

Any suggestions?


#2

Using a variant for the states will save you a good chunk of typing.

type light =
  | Green
  | Yellow
  | Red;

// Your state calls the next light in the cycle
let timerSetFor = x => switch (x) {
  | Green => Yellow
  | Yellow => Red
  | Red => Green
};

type state = {
  timer: Js.Date.t,
  light: light
};

Because of pattern matching you can always iterate through the light types values and generate matching json to correspond with it.

If you want to go Map route, https://bucklescript.github.io/bucklescript/api/Belt.Map.html is probably more robust than Js.Dict (and you can use arbitrary values as keys). It’ll just potentially be more work to integrate with existing JS libs.