Row Polymorphism is a powerful feature of objects in OCaml/ReasonML, that you can use to get around the strict nature of Records. At the same time, as the typing is more loose, it is possible to pass in the wrong type to a function and the compiler will not be able to safe you!

Using open object types you can create functions or methods that accept various types that may only have a single value in common. For example, what if you wanted to require the parameter had a field called name but didn't care about the rest of the data? How would such an open object be defined?

type tx('named) = {
  ..
  getName: string,
} as 'named;


let tellMeYourName: tx('named) => unit = named => {
  Js.log(named#getName);
};
See the .. !

The key to this definition is the double dot .., which indicates that the type is open, thus it can contain other fields and values. This means that the above snippet defines a type with a getName method that returns a string – and any other methods.

The next snippet demonstrates the function declared above being called with various difference types.

class dog (name: string) = {
  as _;
  pub getName = name;
  pub talk = Js.log("Woof!");
};

let myDog = new dog("Spot");
tellMeYourName(myDog);

let makeCat = (name: string, age: int) => {
  as _;
  val mutable catName = name;
  val mutable catAge = age;
  pub getName = catName;
  pub getAge = catAge;
  pub setAge = age => catAge = age;
};

let myCat = makeCat("Cool Cat", 12);
tellMeYourName(myCat);

Note that if you’d rather not have the intermediary type, you can simply define the structure in the function. This reminds me of structural typing in JavaScript.

let tellMeYourNameAgain: { .. getName: string } => unit = 
  named => Js.log(named#getName);

tellMeYourNameAgain(myDog);
tellMeYourNameAgain(myCat);

Note that if you don't include the type in the function definition,  the type for the parameter should be inferred as as open object containing getName.

let tellMeYourNamez = named => {
  print_endline(named#getName);
};
// Type from sketch.sh
// let tellMeYourNamez: {.. getName: string } => unit = <fun>;

It is not possible to pass in a record to a function with an open object parameter, but you could convert to the object type beforehand; however, writing such converters might be the same effort as using varients and pattern matching...

For now, this is the end of my short series on using objects in Reason. I do have some notes on using row polymorphism within a functional program, with any luck I'll post something this week.