Skip to content

Instantly share code, notes, and snippets.

@MysteryMachine
Created November 10, 2015 00:11
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MysteryMachine/0e03cddc3202f5d4c3f6 to your computer and use it in GitHub Desktop.
Save MysteryMachine/0e03cddc3202f5d4c3f6 to your computer and use it in GitHub Desktop.
"use strict";
var mori = require("mori");
var get = mori.get;
var assoc = mori.assoc;
var hashMap = mori.hashMap;
var vector = mori.vector;
var map = mori.map;
var reduce = mori.reduce;
// Polymorphism a la Carte
function initpoly(){
// We are using references to frozen objects as keys to ensure
// that we have unique keys that do not clash.
var typeField = Object.freeze({});
// Protocols are namespaces that contain bags of functions. These functions,
// when called, dispatch on metadata associated with an object.
function Protocol(protocolName, funcNames){
// This function creates a dispatch function for a certain function name,
// and stores that function within an protocol object.
function setUpDispatch(protocolObj, funcName){
// Create a function that takes an instance of a record and some arguments
// This function checks the type of the record, and checks the type
// implementation of that record for some implementation of this
// protocol. If it finds it, it calls that function with the
// recordInstance and the arguments.
function dispatchFunc(recordInstance, args){
// Checks for a type
var implementations = get(recordInstance, typeField);
if(!implementations){
throw(recordInstance + " is not an instance of a Record.");
}
// Digs within that type to find the function implementation for funcName
var implementation = implementations[protocolName];
if(!implementation){
throw(protocolObj +
" does not contain an implementation for " +
protocolName);
}
var interfaceFunc = implementation[funcName];
// And calls that functions
return interfaceFunc(recordInstance, args);
}
// Inserts our newly created function into our protocol object.
protocolObj[funcName] = dispatchFunc;
// Associate new protocol information to the metadata table
return protocolObj;
}
// Create a protocol object
var protocolObj = {name: protocolName};
// For every function that the protocol requires an implementation for, build a
// dispatching function.
funcNames.reduce(setUpDispatch, protocolObj);
// Make the protocol object immutable
return Object.freeze(protocolObj);
};
function Record(recordName, fields, protocolImpls){
// This function associates an implementation of
// a protocol to a metadata object
function addProtocolImplToType(typeObj, protocolName){
// Inside of protocolImpls, find the implementation correlated
// to protocolName
var implementation = protocolImpls[protocolName];
// Copy the implementation and make it immutable
var cpy = Object.freeze(Object.assign({}, implementation));
typeObj[protocolName] = cpy;
return typeObj;
};
// Build type Object for this record
var typeObj = Object.keys(protocolImpls).reduce(addProtocolImplToType, {});
var typeObjFrz = Object.freeze(typeObj);
// Return a constructor that takes an arguments list that maps
// 1 to 1 with our specified fields list. After the object is
// created, we associate type metadata to our record instance.
return function constructor(args){
function addFieldToInstance(recordInstance, field, argsIndex){
// Arguments are initialized positionally, for the nth field
// in fields, assocate into the record instance where the
// nth argument passed in as the value for the key field
return assoc(recordInstance, field, args[argsIndex]);
}
// Associate a new field to the object being constructed for
// every field defined in fields
var recordInst = fields.reduce(addFieldToInstance, hashMap());
// Add type data to our record.
var typedRecordInst = assoc(recordInst, typeField, typeObjFrz);
return typedRecordInst;
};
}
// An implementation is an immutable vanilla JS object
// that maps functions
function Implementation(impl){
var cpy = Object.assign({}, impl);
return Object.freeze(cpy);
}
return Object.freeze({
Protocol: Protocol,
Record: Record,
Implementation: Implementation
});
}
module.exports = initpoly();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment