Skip to content

Instantly share code, notes, and snippets.

@davidchambers
Last active July 20, 2017 14:29
Show Gist options
  • Save davidchambers/863e66275f98b1e2d758152a4dbc3064 to your computer and use it in GitHub Desktop.
Save davidchambers/863e66275f98b1e2d758152a4dbc3064 to your computer and use it in GitHub Desktop.
// This file demonstrates a simpler approach to the problem posed in
// <https://gist.github.com/kurtmilam/ff453e13225580f098d1f64f4bc3e9c7>.
//
// Note that it's possible to define a parametrically polymorphic `move`
// function by using a `Shape s` constraint. On the other hand it is not
// possible to define a parametrically polymorphic `area` function since
// circles and rectangles require different logic.
//
// One could solve this problem by defining a type representative for each
// shape type, which would provide a suitable `area` function (`Z.of` takes
// a type representative for a similar reason). One could then write:
//
// area(Circle, {origin: [0, 0], radius: 1})
//
// Note that `Circle :: Type` would need to be renamed `$Circle` to avoid
// colliding with `Circle :: TypeRep Circle`.
//
// I would argue that `area(Circle)` gains us nothing over `Circle$area`
// so we're better off avoiding the complexity of type representatives.
'use strict';
const R = require('ramda');
const $ = require('sanctuary-def');
const Z = require('sanctuary-type-classes');
const def = $.create({checkTypes: true, env: $.env});
// Point :: Type
const Point = $.Pair($.ValidNumber, $.ValidNumber);
// Circle :: Type
const Circle = $.RecordType({origin: Point, radius: $.ValidNumber});
// Rectangle :: Type
const Rectangle = $.RecordType({origin: Point, dimensions: Point});
// Shape :: TypeClass
const Shape = Z.TypeClass('my-package/Shape', '', [], $.test([], $.RecordType({origin: Point})));
// s :: Type
const s = $.TypeVariable('s');
// move :: Shape s => Point -> s -> s
const move = def('move', {s: [Shape]}, [Point, s, s], ([dx, dy], shape) =>
R.over(R.lensProp('origin'), ([x, y]) => [x + dx, y + dy], shape)
);
// Circle$area :: Circle -> ValidNumber
const Circle$area = def('Circle$area', {}, [Circle, $.ValidNumber], ({radius}) => Math.PI * radius * radius);
// Rectangle$area :: Rectangle -> ValidNumber
const Rectangle$area = def('Rectangle$area', {}, [Rectangle, $.ValidNumber], ({dimensions: [w, h]}) => w * h);
// area :: Any -> ValidNumber !
//
// XXX: Ad hoc polymorphism. Here be dragons!
const area = def('area', {}, [$.Any, $.ValidNumber], x => {
switch (true) {
case $.test([], Circle, x): return Circle$area(x);
case $.test([], Rectangle, x): return Rectangle$area(x);
default: throw new TypeError(`Cannot determine area of ${Z.toString(x)}`);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment