BAM Weblog

Roy - Type-class function constraints

Brian McKenna — 2012-05-06

I’ve gotten Roy’s type-classes to propagate across functions.

To show how it works I’ll define some Monoids:

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

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

instance productMonoid = Monoid {product: Number} {
  append: \x y -> {product: x.product * y.product}
  empty: {product: 1}
}

instance sumMonoid = Monoid {sum: Number} {
  append: \x y -> {sum: x.sum + y.sum}
  empty: {sum: 0}
}

This is the JavaScript output:

var stringMonoid = {
    "append": function(x, y) {
        return x + y;
    },
    "empty": ""
};
var productMonoid = {
    "append": function(x, y) {
        return {
            "product": x.product * y.product
        };
    },
    "empty": {
        "product": 1
    }
};
var sumMonoid = {
    "append": function(x, y) {
        return {
            "sum": x.sum + y.sum
        };
    },
    "empty": {
        "sum": 0
    }
};

With the changes I’ve made this week, functions can use type-classes without specifying an implementation. These functions can now be polymorphic on type-class instances.

For example, implementations of the Monoid type-class are inferred from the call site’s values:

let f x = append empty x
let g x = f x

console.log (g "TEST")
console.log (g {product: 10}).product
console.log (g {sum: 1}).sum

In JavaScript, these functions are compiled to take the type-class instance as an argument. The call site then figures out which instance to pass in:

var f = function(Monoid, x) {
    return Monoid.append(Monoid.empty, x);
};
var g = function(Monoid, x) {
    return f(Monoid, x);
};
console.log(g(stringMonoid, "TEST"));
console.log(g(productMonoid, {
    "product": 10
}).product);
console.log(g(sumMonoid, {
    "sum": 1
}).sum);

The output is:

TEST
10
1
Please enable JavaScript to view the comments powered by Disqus.