BAM Weblog

Roy - Type-classes and how they fix primitives

Brian McKenna2012-04-22

I've managed to hack together an initial version of type-classes in Roy. Here's what's compiling in master:

typeclass Monoid #a {
  append: Function(#a, #a, #a)
  empty: #a
}

instance stringMonoid = Monoid String {
  append: \x y -> x ++ y
  empty: ""
}

console.log (append (append "Hello!" empty) empty)

Which compiles into:

var stringMonoid = {
    "append": function(x, y) {
        return x + y;
    },
    "empty": ""
};
console.log(stringMonoid.append(stringMonoid.append("Hello!", stringMonoid.empty), stringMonoid.empty));

I'm really excited about it because:

  1. Type-classes are very cool
  2. They've been surprisingly easy to implement
  3. They're going to fix many problems in Roy

How are they going to fix problems in Roy?

At the moment there's no built-in way to get a length of a String (yes, I know). This is because the String type is not implemented as a structural type. One obvious fix is to define a String like a structural type:

type String = {
  length: String
  indexOf: Function(String, Number)
  lastIndexOf: Function(String, Number)
  toLowerCase: String
  toUpperCase: String
  ...
}

But that causes a couple of problems:

  1. A String is not fully interchangable with a structural type - it has more information
  2. It's not just a "structure" in JS - so not easy to extend without mutation
  3. What happens when we want to add ES6 support? Suddenly we have to change the compiler built-in String type to add something like startsWith...

Here's the type-class solution:

typeclass WithLength #a {
  length: Function(#a, Number)
}

instance stringWithLength = WithLength String {
  length: \s -> s.length
}

console.log (length "TEST")

This just compiles to:

var stringWithLength = {
    "length": function(s) {
        return s.length;
    }
};
console.log(stringWithLength.length("TEST"));

Easy. An obvious optimisation is to inline that stringWithLength.length function call.

Other things to get working are type-classes with function definitions:

let appendEmptyIsEmpty x = (append x empty) == empty

console.log (appendEmptyIsEmpty "TEST")

Which should become:

var appendEmptyIsEmpty = function(monoid, x) {
    return monoid.append(x, monoid.empty) == monoid.empty;
};

console.log(appendEmptyIsEmpty(stringMonoid, "TEST"));

I'll be spending Atlassian's 20% time on Roy. Expect more exciting things soon.

If you want to play with the master version of Roy, clone the repository from Bitbucket or GitHub.

Please enable JavaScript to view the comments powered by Disqus.