/**
* maryamyriameliamurphies.js
* A library of Haskell-style morphisms ported to ES2015 JavaScript.
*
* type.js
*
* @file Type system foundation.
* @license ISC
*/
/** @module type */
import {partial} from './base';
import {error} from './error';
/**
* The base class for all other types. This class is not meant to be used on its own to instantiate
* new objects, so it does not provide a constructor function of its own, but it does provide some
* default functionality for new data types. The method examples below apply to all children of this
* class and are for illustrative purposes only.
* @alias module:type.Type
* @kind class
* @private
*/
export class Type {
/**
* Return the type signature of an object if it is an instance of this data type. Throw an error
* otherwise. This method is used for type checking and is not meant to be called directly. To get
* the data type of an object, use the `type` function instead or `dataType` if performing your
* own type checking.
* @param {Type} a - An instance of this type class
* @returns {string} The type
* @alias module:type.Type.type
* @example
* const lst = list(1,2,3);
* List.type(lst); // => List
* const tup = tuple(1,2);
* Tuple.type(tup); // => (number,number)
* const m = just(5);
* Maybe.type(m); // => Maybe
*/
static type(a) { return dataType(a) === this ? this.name : error.typeError(a, this.type); }
/**
* Return the string representation of an object for a given data type.
* @returns {string} The data type as a string
* @alias module:type.Type.toString
* @example
* const tup = tuple(1,2);
* tup.toString(); // => [Object Tuple]
* const lst = list(1,2,3);
* lst.toString(); // => [Object List]
* const m = just(5);
* m.toString(); // => Just 5
*/
toString() { return this.valueOf(); }
/**
* Return the type of an object for a given data type.
* @returns {string} The type of the object
* @alias module:type.Type.typeOf
* @example
* const tup = tuple(1,2);
* tup.typeOf(); // => (number,number)
* const lst = list(1,2,3);
* lst.typeOf(); // => [number]
* const m = just(5);
* m.typeOf(); // => Maybe number
*/
typeOf() { return dataType(this).name; }
/**
* Return the value of an object for a given data type.
* @returns {string} The value of the object.
* @alias module:type.Type.valueOf
* @example
* const tup = tuple(1,2);
* tup.valueOf(); // => (1,2)
* const lst = list(1,2,3);
* lst.valueOf(); // => [1:2:3:[]]
* const m = just(5);
* m.valueOf(); // => Just 5
*/
valueOf() { return this; }
}
/**
* Return a closure that checks whether a given object is a member of a predefined type class. Note
* that the library only checks for the existence of the required property or properties. Whether or
* not those properties are functions and whether or not they return the values expected by the type
* class are not verified. This is a utility function for defining new type classes.
* @param {...string} methods - A comma separated list of functions that a data type must define in
* order to be a member of the type class defined by this function
* @returns {Function} A closure that returns true if a given object implements all the specified
* methods, false otherwise
* @kind function
* @example
* // require that instances of the `Eq` type class define an `isEq` function:
* const Eq = defines(`isEq`);
*
* // require that instances of `Traversable` define `traverse` and are also instances of `Functor`
* // and `Foldable`:
* const Traversable = defines(`fmap`, `foldr`, `traverse`);
*/
export const defines = (...methods) => a => methods.every(m => m in dataType(a));
/**
* Return the data type of a given object. In JavaScript, this is simply the object's constructor,
* so this function essentially serves as an alias for terminological clarification.
* @param {*} a - Any object
* @returns {Function} The object's constructor function
* @kind function
* @example
* dataType(0); // function Number() { [native code] }
* const lst = list(1,2,3);
* dataType(lst); // => function List(head, tail) { ... }
* lst.typeOf(); // => List // more useful if you don't need a function pointer
*/
export const dataType = a => a.constructor;
/**
* Return the type of any object as specified by this library or, otherwise, its primitive type.
* @param {*} a - Any object
* @returns {string} The type of the object
* @kind function
* @example
* type(0); // => number
* const t = tuple(1,2);
* type(t); // => (number,number)
*/
export const type = a => a instanceof Type ? a.typeOf() : typeof a;
/**
* Determine whether two objects are the same type. Return `true` if they are and `false` otherwise.
* @param {*} a - Any object
* @param {*} b - Any object
* @returns {boolean} `true` if the two objects are the same type, `false` otherwise
* @kind function
* @example
* typeCheck(0, 1); // => true
* typeCheck(0, `a`); // => false
*/
export const typeCheck = (a, b) => {
const typeCheck_ = (a, b) => {
if (a instanceof Type && b instanceof Type) {
return dataType(a).type(a) === dataType(b).type(b);
}
if (dataType(a) === dataType(b)) { return true; }
return false;
}
return partial(typeCheck_, a, b);
}