1

In the bs-json library there is an example provided for converting a json structure to a tree using the andThen combinator. The original example can be found here on github in the ML interface file. Copying the function decodeTree from this file to the browser reason tools shows a syntax error.

Would very much appreciate any pointers for getting this to work.

My attempt at translating it to Reason3 results in a type error. This is the code:

type tree('a) =
  | Node('a, list(tree('a)))
  | Leaf('a);

let json = {| {
  "type": "node",
  "value": 9
  "children": [{
    "type": "leaf",
    "value": 5,
    "children": [{
      "type": "leaf",
      "value": 3
    }, {
      "type": "leaf",
      "value": 2
    }]
  }, {
      "type": "leaf",
      "value": 4
  }]
} |};

let decodeTree = (decodeValue, json) =>
  Json.Decode.(
    field("type", string)
    |> andThen(type_ =>
         switch type_ {
         | "node" =>
           Node(
             field("value", decodeValue),
             field("children", children =>
               children |> array(decodeTree) |> map(Array.to_list)
             )
           )
         | "leaf" => Leaf(field("value", decodeValue))
         }
       )
  );

let myTree = json |> Json.parseOrRaise |> decodeTree(Json.Decode.int);

This is the type error,

This has type:
   tree('a)
But somewhere wanted:
   Json.Decode.decoder('b) (defined as (Js.Json.t) => 'b)
glennsl
  • 28,186
  • 12
  • 57
  • 75
jcsherin
  • 368
  • 1
  • 6

1 Answers1

1

Sorry about that. Not entirely sure what was going on with that example. Here's one that should work:

/* Decode a JSON tree structure */
type tree('a) =
  | Node('a, list(tree('a)))
  | Leaf('a);

module Decode = {
  open Json.Decode;

  let rec tree = decoder =>
    field("type", string) |> andThen(
       fun | "node" => node(decoder)
           | "leaf" => leaf(decoder)
           | _      => failwith("unknown node type")
     )

  and node = (decoder, json) =>
    Node(
      json |> field("value", decoder),
      json |> field("children", array(tree(decoder)) |> map(Array.to_list))
    )

  and leaf = (decoder, json) =>
    Leaf(json |> field("value", decoder));
};

let json = {| {
  "type": "node",
  "value": 9,
  "children": [{
    "type": "node",
    "value": 5,
    "children": [{
      "type": "leaf",
      "value": 3
    }, {
      "type": "leaf",
      "value": 2
    }]
  }, {
      "type": "leaf",
      "value": 4
  }]
} |};

let myTree =
  json |> Json.parseOrRaise
       |> Decode.tree(Json.Decode.int);

Edit: The specific error you have is caused by andThen expecting a decoder, but being given a tree('a) (Node or Leaf). All that's needed to turn it into a decoder is to add a json argument, which you'd pass to the field decoders:

|> andThen((type_, json) =>
     switch type_ {
     | "node" =>
       Node(
         json |> field("value", decodeValue),
         json |> field("children", children =>
           children |> array(decodeTree) |> map(Array.to_list)
         )
       )
     | "leaf" => json |> Leaf(field("value", decodeValue))
     }
   )
glennsl
  • 28,186
  • 12
  • 57
  • 75