From b3c5cc33aa3a7188420999f13f6e7b53d2a14e89 Mon Sep 17 00:00:00 2001 From: James Earl Douglas Date: Sat, 22 Aug 2015 12:36:55 -0700 Subject: [PATCH] Add ReaderT --- package.json | 2 +- src/teep.ts | 51 ++++++++++++++++++++++++++++++++++++------------ teep.js | 28 +++++++++++++++++++++++++- test/examples.js | 47 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 113 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 5321312..2d90d4c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "teep", "description": "A JavaScript library for functional programming.", - "version": "1.0.0", + "version": "1.1.0", "homepage": "https://github.com/earldouglas/teep", "author": { "name": "James Earl Douglas", diff --git a/src/teep.ts b/src/teep.ts index 5610470..9e377f3 100644 --- a/src/teep.ts +++ b/src/teep.ts @@ -26,7 +26,7 @@ module edc { put: (A, B) => void; } - class DumbCache implements Cache { + class DumbCache { cache = {}; get = (k) => { return this.cache[k]; }; put = (k,v) => { this.cache[k] = v; }; @@ -78,7 +78,12 @@ module edc { }, }; - export interface Option { + export interface Monad { + map: (f: (A) => B) => Monad; + flatMap: (f: (A) => Monad) => Monad; + } + + export interface Option extends Monad { empty : boolean; map : (f: (A) => B) => Option; flatMap : (f: (A) => Option) => Option; @@ -115,7 +120,7 @@ module edc { } } - export interface Validation { + export interface Validation extends Monad { valid : boolean; map : (f: (A) => B) => Validation; flatMap : (f: (A) => Validation) => Validation; @@ -151,7 +156,7 @@ module edc { invalid: (errors: Array) => { return new Invalid(errors); }, }; - export interface List { + export interface List extends Monad { length : number; map : (f: (A) => B) => List; flatMap : (f: (A) => List) => List; @@ -273,15 +278,38 @@ module edc { var future = (f: (A) => any) => { return new Future(f); } + class ReaderT { + f: (A) => Monad; + constructor(f: (A) => Monad) { + this.f = f; + } + apply(a: A): Monad { + return this.f(a); + }; + map(g: (B) => C): ReaderT { + return new ReaderT((a) => { + return this.f(a).map(g); + }); + }; + flatMap(g: (B) => ReaderT): ReaderT { + return new ReaderT((a) => { + return this.f(a).map(g).flatMap((r) => { return r.apply(a); }); + }); + }; + } + + var readerT = (f: (A) => Monad) => { return new ReaderT(f); } + export var teep = { - array: array, - fn: fn, - option: option, + array: array, + fn: fn, + option: option, validation: validation, - list: list, - promise: promise, - reader: reader, - future: future, + list: list, + promise: promise, + reader: reader, + future: future, + readerT: readerT, }; var setExports = function () { @@ -296,4 +324,3 @@ module edc { !exports || setExports(); } - diff --git a/teep.js b/teep.js index 72daa0a..0cb8658 100644 --- a/teep.js +++ b/teep.js @@ -253,6 +253,31 @@ var edc; return Future; })(); var future = function (f) { return new Future(f); }; + var ReaderT = (function () { + function ReaderT(f) { + this.f = f; + } + ReaderT.prototype.apply = function (a) { + return this.f(a); + }; + ; + ReaderT.prototype.map = function (g) { + var _this = this; + return new ReaderT(function (a) { + return _this.f(a).map(g); + }); + }; + ; + ReaderT.prototype.flatMap = function (g) { + var _this = this; + return new ReaderT(function (a) { + return _this.f(a).map(g).flatMap(function (r) { return r.apply(a); }); + }); + }; + ; + return ReaderT; + })(); + var readerT = function (f) { return new ReaderT(f); }; edc.teep = { array: array, fn: fn, @@ -261,7 +286,8 @@ var edc; list: list, promise: promise, reader: reader, - future: future + future: future, + readerT: readerT }; var setExports = function () { for (var i in edc.teep) { diff --git a/test/examples.js b/test/examples.js index 72a68d1..65d176a 100644 --- a/test/examples.js +++ b/test/examples.js @@ -288,7 +288,6 @@ describe('examples', function () { }); - describe('future', function () { var async = function(x) { return function (k) { @@ -337,4 +336,50 @@ describe('examples', function () { }); + describe('readerT', function () { + + var db = { + answer: 42, + }; + + var getAnswer = function (db) { + return teep.future(function (k) { + return k(db.answer); + }); + }; + + function addToAnswer(x) { + return teep.readerT(function (db) { + return teep.future(function (k) { + return k(db.answer + x); + }); + }); + }; + + function verify(x, done) { + return function (y) { + if (x === y) { + done(); + } + }; + }; + + it('apply', function (done) { + teep.readerT(getAnswer).apply(db).apply(verify(42, done)); + }); + + it('map', function (done) { + teep.readerT(getAnswer).map(function (x) { + return x + 1; + }).apply(db).apply(verify(43, done)); + }); + + it('flatMap', function (done) { + teep.readerT(getAnswer).flatMap(function (x) { + return addToAnswer(x + 1); + }).apply(db).apply(verify(85, done)); + }); + + }); + });