Created
November 10, 2015 00:11
-
-
Save MysteryMachine/0e03cddc3202f5d4c3f6 to your computer and use it in GitHub Desktop.
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
"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