Last active
July 20, 2017 14:29
-
-
Save davidchambers/863e66275f98b1e2d758152a4dbc3064 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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