Roy - Type-classes and how they fix primitives
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:
- Type-classes are very cool
- They’ve been surprisingly easy to implement
- 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:
- A String is not fully interchangable with a structural type - it has more information
- It’s not just a “structure” in JS - so not easy to extend without mutation
- 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.