How do I declare a map type in Reason ML?
Asked Answered
C

2

14

One advantage of Reason ML over JavaScript is that it provides a Map type that uses structural equality rather than reference equality.

However, I cannot find usage examples of this.

For example, how would I declare a type scores that is a map of strings to integers?

/* Something like this */
type scores = Map<string, int>; 

And how would I construct an instance?

/* Something like this */
let myMap = scores();

let myMap2 = myMap.set('x', 100);
Cuneo answered 16/2, 2018 at 16:12 Comment(0)
C
21

The standard library Map is actually quite unique in the programming language world in that it is a module functor which you must use to construct a map module for your specific key type (and the API reference documentation is therefore found under Map.Make):

module StringMap = Map.Make({
  type t = string;
  let compare = compare
});

type scores = StringMap.t(int);

let myMap = StringMap.empty;
let myMap2 = StringMap.add("x", 100, myMap);

There are other data structures you can use to construct map-like functionality, particularly if you need a string key specifically. There's a comparison of different methods in the BuckleScript Cookbook. All except Js.Dict are available outside BuckleScript. BuckleScript also ships with a new Map data structure in its beta standard library which I haven't tried yet.

Carlist answered 16/2, 2018 at 17:50 Comment(4)
So the key type is defined in the "module functor" (that's a new concept for me) and the value type is define as a type parameter to the t function? Can you share why it was implemented in two stages? Coming from F# this seems very strange.Cuneo
StringMap.t is a type with a type parameter, not a function in the strict sense, though conceptually you can (and should!) think of it as a function over types. Similarly, a module functor is just a function over modules, it takes a module (or several) and returns a module.Carlist
I'm not entirely sure why it was implemented as a functor, but one possible motivation is simply because you can. In F# I don't think you can, because the module system just isn't powerful enough. For an immutable data structure it probably also makes the implementation cleaner, slightly more performant and less memory-hogging, since without "hard-coding" the compare function you'd have to carry that around in a wrapper that has to be duplicated everytime you add or remove a node from the map.Carlist
Another important point is that the type of the map should depend on both the key type and the comparison function. Otherwise it would be possible to try to merge two maps built with a different comparison functions leading to a completely erratic behavior. Functor provides a nice way to make the type of map dependent on the whole parameter module, comparison function included. The other clean ways to achieve this result are more intricated (cf. Janestreet's Base.Map module for a good example)Chlorohydrin
P
6

If you're just dealing with a Map<string, int>, Belt's Map.String would do the trick.

module MS = Belt.Map.String;

let foo: MS.t(int) = [|("a", 1), ("b", 2), ("c", 3)|]->MS.fromArray;

The ergonomics around the Belt version are a little less painful, and they're immutable maps to boot! There's also Map.Int within Belt. For other key types, you'll have to define your own comparator. Which is back to something similar to the two step process @glennsl detailed above.

Plunk answered 28/11, 2018 at 19:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.