Returning a Map over FFI with argonaut

I’m having trouble figuring out how to return a Map from JavaScript to PureScript. I assume converting to JSON with argonaut would be a good intermediate step, as described in these FFI tips, but decoding doesn’t work, despite having a representation that looks identical between PS and JS in the logs.


"use strict";

exports.mapSetFooJson = m => {
  let n = new Map(m);
  n.set("Foo", 42);
  let s = JSON.stringify(Array.from(n));
  console.log("JS JSON: " + s);
  return s;


foreign import mapSetFooJson :: Json -> Json

mapSetFoo :: Map String Int -> Either String (Map String Int)
mapSetFoo m = decodeJson $ mapSetFooJson $ encodeJson m

myMap :: Map String Int
myMap = fromFoldable [ Tuple "hat" 1, Tuple "cat" 2 ]

main :: Effect Unit
main = do
  logShow myMap
  log $ "PS JSON: " <> (stringify $ encodeJson myMap)
  logShow $ mapSetFoo myMap


$ spago run

(fromFoldable [(Tuple "cat" 2),(Tuple "hat" 1)])
PS JSON: [["cat",2],["hat",1]]
JS JSON: [["cat",2],["hat",1],["Foo",42]]
(Left "Couldn't decode List: Value is not an Array")

Here’s the project containing the above snippet.

This works if omitting the stringify step on the JS side.

  //let s = JSON.stringify(Array.from(n));
  let s = Array.from(n)

This makes sense in hindsight, since there’s no corresponding jsonParser on the PS side.

Still wondering why the Array.from step is required. I assume it’s related to this question.

Json only consists of booleans, numbers, strings, and arrays and objects of other Json values; a JS Map isn’t any of those, so typing an FFI function which returns a Map as returning Json is probably going to break things when you try to consume that value.