Last active
June 26, 2016 21:05
-
-
Save jethrolarson/afdf6ed67b5d2cfc8d8d to your computer and use it in GitHub Desktop.
AGeneric interface fantasy-land objects
This file contains hidden or 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
var Maybe = require('./maybe.js') | |
var List = require('./list.js') | |
//Pass fantasy implementations to it | |
var {concat, map} = require('fantasy')({Maybe, List}) | |
mapDouble = map(a => 2 * a) | |
mapDouble([1, 2]) | |
//[2, 4] | |
mapDouble(Maybe.Just(3)) | |
//{'@@type': 'Maybe', value: 6} | |
map(mapDouble)([Just(1), Nothing()]) | |
//[{'@@type': 'Maybe', value: 2}, {'@@type': 'Maybe', value: null}] | |
concat([1])([2]) | |
// [1, 2] | |
concat(Just(1))(Just(2)) | |
// Error: Function (concat) is not implemented for (Maybe) |
This file contains hidden or 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
//@@type is just proposed convention | |
//:: a -> String | |
const defaultEvaluator = a => | |
var type = a['@@type'] | |
if(type){ | |
return type | |
} | |
} | |
// Get the name of an algebraic type's type. | |
//:: [(a -> String)] -> a -> String | |
const ftype = evaluators => a => { | |
// check the value against provided evaluator functions | |
for(var i = 0; i < evaluators.length; i++){ | |
var result = evaluators[i](a) | |
if(result){ | |
return result | |
} | |
} | |
return '' | |
} | |
//:: String -> (a -> Boolean) -> String | |
const evaluator = (name, f) => x => f(x) ? name : '' | |
// Collects implementations of `is` for type matching | |
//:: {{typeEvaluator: (a -> Boolean)}} -> [(a -> String)] | |
const getTypeEvaluators = xface => { | |
var typeEvaluators = [] | |
for(var k in xface){ | |
if(xface.hasOwnProperty(k) && xface[k].typeEvaluator){ | |
typeEvaluators.push(evaluator(k, xface[k].typeEvaluator)) | |
} | |
} | |
return typeEvaluators | |
} | |
// xface is a map from types to their implementations | |
module.exports = xface => { | |
if(!xface) throw "no implementations of fantasy interface are provided" | |
const evaluators = [defaultEvaluator].concat(getTypeEvaluators(xface)) | |
//:: a -> String | |
const checkType = ftype(evaluators) | |
//:: String, a -> (a -> b) | |
const getMethod = (name, a) => { | |
var type = checkType(a) | |
if(!type) throw 'Type is not defined' | |
var namespace = xface[type] | |
if(!namespace) throw `Type (${type}) is not registered`; | |
var method = namespace[name] | |
if(!method) throw `Function (${name}) is not implemented for (${type})` | |
return method | |
} | |
//These specify which argument should be used to ascertain the type | |
const dispatch = name => a => getMethod(name, a)(a) | |
const dispatch2 = name => a => b => getMethod(name, b)(a)(b) | |
const dispatch3 = name => a => b => c => getMethod(name, c)(a)(b)(c) | |
//TODO figure out how to extend this api externally. (Open/Closed Principle) | |
// PUBLIC API | |
//----------- | |
return { | |
type: checkType, | |
dispatch, dispatch2, dispatch3, | |
// Setoid | |
//:: S a -> S b -> Boolean | |
equals: dispatch('equals'), | |
// Semigroup | |
//:: S a -> S b -> S c | |
concat: dispatch('concat'), | |
// Monoid | |
// M a -> M _ | |
empty: dispatch('empty'), | |
// Functor | |
//:: (a -> b) -> F a -> F b | |
map: dispatch2('map'), | |
// Apply | |
//:: F (a -> b) -> F a -> F b | |
ap: dispatch2('ap'), | |
//Foldable | |
//:: (b -> a -> b) -> b -> [a] -> b | |
reduce: dispatch3('reduce'), | |
// Monad | |
// Satisfied by chain and applicative | |
// Applicative | |
//:: F a -> b -> F b | |
of: dispatch('of'), | |
// Chain | |
// a.k.a flatmap or bind | |
//:: (a -> M b) -> M a -> M b | |
chain: dispatch2('chain'), | |
//Extract | |
//:: M a -> a | |
extract: dispatch('extract') | |
} | |
} |
This file contains hidden or 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
//example partial implementation of list | |
module.exports = { | |
typeEvaluator: val => | |
(val != null && | |
val.length >= 0 && | |
Object.prototype.toString.call(val) === '[object Array]') | |
, map: f => list => list.map(f) | |
, empty: list => [] | |
, concat: a => b => a.concat(b) | |
} |
This file contains hidden or 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
//example partial implementation of maybe | |
const nothing = {'@@type': 'Maybe', value: null} | |
const Just = a => {'@@type': 'Maybe', value: a} | |
const Nothing = ()=> nothing | |
module.exports = { | |
map: f => m => m.value != null ? Just(f(m.value)) : nothing | |
, empty: m => nothing | |
, Just | |
, Nothing | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment