From 50d6473235a338e7aee3434df2be7068d3e45bfa Mon Sep 17 00:00:00 2001 From: Chris Myers Date: Mon, 23 Sep 2013 21:59:43 +0100 Subject: [PATCH 01/12] Added list --- example/List.html | 77 ++++++++++++++++++++++++++++++++++++ src/main/javascript/monad.js | 32 ++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 example/List.html diff --git a/example/List.html b/example/List.html new file mode 100644 index 0000000..fd1ede0 --- /dev/null +++ b/example/List.html @@ -0,0 +1,77 @@ + + + + + + + + Example IO page + + + +Some text + +
+ first name: + last name: + postcode: +
+ +
+ Result: +
+ + + + + \ No newline at end of file diff --git a/src/main/javascript/monad.js b/src/main/javascript/monad.js index 4a9a032..f97c1c2 100644 --- a/src/main/javascript/monad.js +++ b/src/main/javascript/monad.js @@ -313,13 +313,41 @@ } } - Function.prototype.andThen = function(g) { + Function.prototype.andThen = function (g) { var f = this - return function(x) { + return function (x) { return g(f(x)) } } + var List = list = window.List = function (head, tail) { + return new List.fn.init(head, tail) + } + + + + List.fn = List.prototype = { + init: function (head, tail) { + if (head == undefined || head == null) { + this.isNil = true + } else { + this.isNil = false + this.head = head + this.tail = tail + } + }, + cons: function(head) { + return List(head, this) + } + } + + List.fn.init.prototype = List.fn; + var Nil = window.Nil = new List.fn.init() + + Array.prototype.list = function() { + + } + return this }(window || this)); From 7610a590b218e4474aa72ec64bff2f720a9575c4 Mon Sep 17 00:00:00 2001 From: Chris Myers Date: Thu, 26 Sep 2013 22:15:13 +0100 Subject: [PATCH 02/12] Added functions to convert between List and JavaScript Array --- src/main/javascript/monad.js | 29 ++++++++++++++++++++++++++--- src/test/javascript/list_spec.js | 12 ++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 src/test/javascript/list_spec.js diff --git a/src/main/javascript/monad.js b/src/main/javascript/monad.js index f97c1c2..81c97d7 100644 --- a/src/main/javascript/monad.js +++ b/src/main/javascript/monad.js @@ -324,7 +324,17 @@ return new List.fn.init(head, tail) } + var listMap = function (fn, l) { + if (l.isNil) { + return l + } else { + return listMap(fn, l.tail).cons(fn(l.head)) + } + } + var foldLeft = function (fn, acc, l) { + return l.isNil ? acc : foldLeft(fn, fn(acc, l.head), l.tail) + } List.fn = List.prototype = { init: function (head, tail) { @@ -336,16 +346,29 @@ this.tail = tail } }, - cons: function(head) { + cons: function (head) { return List(head, this) + }, + map: function (fn) { + return listMap(fn, this) + }, + toArray: function () { + return foldLeft(function (acc, e) { + acc.push(e) + return acc + }, [], this) } } List.fn.init.prototype = List.fn; var Nil = window.Nil = new List.fn.init() - Array.prototype.list = function() { - + Array.prototype.list = function () { + var l = Nil + for (i = this.length; i--; i <= 0) { + l = l.cons(this[i]) + } + return l } diff --git a/src/test/javascript/list_spec.js b/src/test/javascript/list_spec.js new file mode 100644 index 0000000..e48637a --- /dev/null +++ b/src/test/javascript/list_spec.js @@ -0,0 +1,12 @@ +describe("An immutable list", function () { + var list = List(1, List(2, List(3, List(4, Nil)))) + it("can be mapped", function () { + var mappedList = list.map(function (a) { + return a + 1 + }) + expect(mappedList.head).toBe(2) + expect(mappedList.tail.head).toBe(3) + expect(mappedList.tail.tail.head).toBe(4) + expect(mappedList.tail.tail.tail.head).toBe(5) + }) +}) \ No newline at end of file From fbdfc4e81b22cfdec91d126206a9cecf184180df Mon Sep 17 00:00:00 2001 From: Chris Myers Date: Thu, 26 Sep 2013 22:20:43 +0100 Subject: [PATCH 03/12] More Tests --- src/test/javascript/list_spec.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/javascript/list_spec.js b/src/test/javascript/list_spec.js index e48637a..ffac891 100644 --- a/src/test/javascript/list_spec.js +++ b/src/test/javascript/list_spec.js @@ -1,5 +1,14 @@ describe("An immutable list", function () { var list = List(1, List(2, List(3, List(4, Nil)))) + + it("can be converted to Array", function() { + expect(list.toArray()).toEqual([1,2,3,4]) + }) + + it("can be created from an Array", function(){ + expect([1,2,3,4].list()).toEqual(list) + }) + it("can be mapped", function () { var mappedList = list.map(function (a) { return a + 1 @@ -9,4 +18,8 @@ describe("An immutable list", function () { expect(mappedList.tail.tail.head).toBe(4) expect(mappedList.tail.tail.tail.head).toBe(5) }) + + it("can be reduced using foldLeft", function() { + + }) }) \ No newline at end of file From 0408836507218f4b92cd16494226f842141bf444 Mon Sep 17 00:00:00 2001 From: bskyb-myersch Date: Fri, 27 Sep 2013 17:31:09 +0100 Subject: [PATCH 04/12] Added foldLeft --- src/main/javascript/monad.js | 6 ++++++ src/test/javascript/list_spec.js | 12 ++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/javascript/monad.js b/src/main/javascript/monad.js index 81c97d7..457e135 100644 --- a/src/main/javascript/monad.js +++ b/src/main/javascript/monad.js @@ -357,6 +357,12 @@ acc.push(e) return acc }, [], this) + }, + foldLeft: function(initialValue) { + var self = this + return function(fn) { + return foldLeft(fn, initialValue, self) + } } } diff --git a/src/test/javascript/list_spec.js b/src/test/javascript/list_spec.js index ffac891..74188fb 100644 --- a/src/test/javascript/list_spec.js +++ b/src/test/javascript/list_spec.js @@ -1,6 +1,12 @@ describe("An immutable list", function () { + var list = List(1, List(2, List(3, List(4, Nil)))) + var plusOne = function (a) { + return a + 1 + }; + + it("can be converted to Array", function() { expect(list.toArray()).toEqual([1,2,3,4]) }) @@ -10,9 +16,7 @@ describe("An immutable list", function () { }) it("can be mapped", function () { - var mappedList = list.map(function (a) { - return a + 1 - }) + var mappedList = list.map(plusOne) expect(mappedList.head).toBe(2) expect(mappedList.tail.head).toBe(3) expect(mappedList.tail.tail.head).toBe(4) @@ -20,6 +24,6 @@ describe("An immutable list", function () { }) it("can be reduced using foldLeft", function() { - + expect(list.foldLeft(0)(function(acc,e) {return acc+e})).toEqual(10) }) }) \ No newline at end of file From aa6bb96907f9e78ab5386a33d3b6f7a271cef828 Mon Sep 17 00:00:00 2001 From: bskyb-myersch Date: Tue, 1 Oct 2013 13:46:34 +0100 Subject: [PATCH 05/12] Added foldRight, flatten, flatMap and append for lists --- src/main/javascript/monad.js | 28 ++++++++++++++++++++++++++-- src/test/javascript/list_spec.js | 18 ++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/main/javascript/monad.js b/src/main/javascript/monad.js index 457e135..f7e11d9 100644 --- a/src/main/javascript/monad.js +++ b/src/main/javascript/monad.js @@ -336,6 +336,16 @@ return l.isNil ? acc : foldLeft(fn, fn(acc, l.head), l.tail) } + var foldRight = function (fn, l, acc) { + return l.isNil ? acc : fn(l.head, foldRight(fn, l.tail, acc)) + } + + var append = function (list1, list2) { + return list1.isNil ? list2 : append(list1.tail, list2).cons(list1.head) + + } + + List.fn = List.prototype = { init: function (head, tail) { if (head == undefined || head == null) { @@ -358,11 +368,21 @@ return acc }, [], this) }, - foldLeft: function(initialValue) { + foldLeft: function (initialValue) { var self = this - return function(fn) { + return function (fn) { return foldLeft(fn, initialValue, self) } + }, + append: function (list2) { + return append(this, list2) + }, + flatten: function () { + return foldRight(append, this, Nil) + + }, + flatMap: function (fn) { + return this.map(fn).flatten() } } @@ -377,6 +397,10 @@ return l } + Object.prototype.cons = function (list) { + return list.cons(this) + } + return this }(window || this)); diff --git a/src/test/javascript/list_spec.js b/src/test/javascript/list_spec.js index 74188fb..6f1a6ae 100644 --- a/src/test/javascript/list_spec.js +++ b/src/test/javascript/list_spec.js @@ -26,4 +26,22 @@ describe("An immutable list", function () { it("can be reduced using foldLeft", function() { expect(list.foldLeft(0)(function(acc,e) {return acc+e})).toEqual(10) }) + + it("will have cons available on objects", function() { + expect("fun".cons(list).toArray()).toEqual(["fun",1,2,3,4]) + }) + + it("will be transformed by a flatMap", function() { + expect(list.flatMap(function (e) { + return [e*e, e+e].list() + }).toArray()).toEqual([1,2,4,4,9,6,16,8]) + }) + + it("will be append another list", function(){ + expect(list.append([5,6,7].list()).toArray()).toEqual([1,2,3,4,5,6,7]) + }) + + it("will flatten inner lists", function() { + expect([[1,2].list(),[3,4].list()].list().flatten().toArray()).toEqual([1,2,3,4]) + }) }) \ No newline at end of file From 30d452abbfd267bc02e28f85b3022d66c3fdeec6 Mon Sep 17 00:00:00 2001 From: Chris Myers Date: Fri, 4 Oct 2013 07:20:35 +0100 Subject: [PATCH 06/12] Starting on sequence --- src/main/javascript/monad.js | 14 ++++++- src/test/javascript/list_spec.js | 66 ++++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/src/main/javascript/monad.js b/src/main/javascript/monad.js index f7e11d9..ba062a6 100644 --- a/src/main/javascript/monad.js +++ b/src/main/javascript/monad.js @@ -27,7 +27,7 @@ return (val == undefined || val == null) ? Maybe.none() : Maybe.some(val) }; - var Some = Just = Maybe.Just = Maybe.just = Maybe.Some = Maybe.some = function (val) { + var Some = Just = Maybe.Just = Maybe.just = Maybe.Some = Maybe.some = window.Some = window.Just = function (val) { return new Some.fn.init(val) }; @@ -342,7 +342,12 @@ var append = function (list1, list2) { return list1.isNil ? list2 : append(list1.tail, list2).cons(list1.head) + } + var sequenceMaybe = function (list) { + return list.head.map(List).flatMap(function (list1) { + return sequenceMaybe(list.tail) + }) } @@ -353,7 +358,7 @@ } else { this.isNil = false this.head = head - this.tail = tail + this.tail = (tail == undefined || tail == null) ? Nil : tail } }, cons: function (head) { @@ -383,6 +388,11 @@ }, flatMap: function (fn) { return this.map(fn).flatten() + }, + // transforms a list of Maybes to a Maybe list + sequenceMaybe: function () { + return sequenceMaybe(this) + } } diff --git a/src/test/javascript/list_spec.js b/src/test/javascript/list_spec.js index 6f1a6ae..f6090d5 100644 --- a/src/test/javascript/list_spec.js +++ b/src/test/javascript/list_spec.js @@ -1,5 +1,22 @@ describe("An immutable list", function () { + beforeEach(function () { + this.addMatchers({ + toBeSomeMaybe: function (expected) { + return this.actual.isSome(); + }, + toBeSomeMaybeWith: function (expected) { + return this.actual.some() == expected + }, + toBeSomeMaybeWithList: function (expected) { + return this.actual.some().toArray() === expected + }, + toBeNoneMaybe: function () { + return this.actual.isNone() + } + }); + }) + var list = List(1, List(2, List(3, List(4, Nil)))) var plusOne = function (a) { @@ -7,12 +24,12 @@ describe("An immutable list", function () { }; - it("can be converted to Array", function() { - expect(list.toArray()).toEqual([1,2,3,4]) + it("can be converted to Array", function () { + expect(list.toArray()).toEqual([1, 2, 3, 4]) }) - it("can be created from an Array", function(){ - expect([1,2,3,4].list()).toEqual(list) + it("can be created from an Array", function () { + expect([1, 2, 3, 4].list()).toEqual(list) }) it("can be mapped", function () { @@ -23,25 +40,44 @@ describe("An immutable list", function () { expect(mappedList.tail.tail.tail.head).toBe(5) }) - it("can be reduced using foldLeft", function() { - expect(list.foldLeft(0)(function(acc,e) {return acc+e})).toEqual(10) + it("can be reduced using foldLeft", function () { + expect(list.foldLeft(0)(function (acc, e) { + return acc + e + })).toEqual(10) }) - it("will have cons available on objects", function() { - expect("fun".cons(list).toArray()).toEqual(["fun",1,2,3,4]) + it("will have cons available on objects", function () { + expect("fun".cons(list).toArray()).toEqual(["fun", 1, 2, 3, 4]) }) - it("will be transformed by a flatMap", function() { + it("will be transformed by a flatMap", function () { expect(list.flatMap(function (e) { - return [e*e, e+e].list() - }).toArray()).toEqual([1,2,4,4,9,6,16,8]) + return [e * e, e + e].list() + }).toArray()).toEqual([1, 2, 4, 4, 9, 6, 16, 8]) }) - it("will be append another list", function(){ - expect(list.append([5,6,7].list()).toArray()).toEqual([1,2,3,4,5,6,7]) + it("will be append another list", function () { + expect(list.append([5, 6, 7].list()).toArray()).toEqual([1, 2, 3, 4, 5, 6, 7]) + }) + + describe("will flatten inner lists", function() { + it("with two elements", function () { + expect([[1, 2].list(), [3, 4].list()].list().flatten().toArray()).toEqual([1, 2, 3, 4]) + }) + it("with one element", function() { + expect([[1,2].list()].list().flatten().toArray()).toEqual([1,2]) + }) + }) - it("will flatten inner lists", function() { - expect([[1,2].list(),[3,4].list()].list().flatten().toArray()).toEqual([1,2,3,4]) + describe("will sequence a list", function () { + describe("of Maybes", function () { +// it("with one defined element", function () { +// expect(List(Some("hello")).sequenceMaybe().some().toArray()).toEqual(["hello"]) +// }) +// it("with multiple defined elements", function() { +// expect(List(Some(1),Some(2),Some(3)).sequenceMaybe().some().toArray()).toBeSomeMaybeWithList([1,2,3]) +// }) + }) }) }) \ No newline at end of file From 49c6817cde07c7f966dec2343414730fc8848be5 Mon Sep 17 00:00:00 2001 From: bskyb-myersch Date: Fri, 4 Oct 2013 10:42:24 +0100 Subject: [PATCH 07/12] Added sequence for list of Maybe monads --- src/main/javascript/monad.js | 29 ++++++++++++++++++++++++++--- src/test/javascript/list_spec.js | 15 +++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/main/javascript/monad.js b/src/main/javascript/monad.js index ba062a6..7cdabd3 100644 --- a/src/main/javascript/monad.js +++ b/src/main/javascript/monad.js @@ -31,6 +31,16 @@ return new Some.fn.init(val) }; + Maybe.map2 = function (maybeA, maybeB) { + return function (fn) { + return maybeA.flatMap(function (a) { + return maybeB.map(function (b) { + return fn(a, b) + }) + }) + } + } + Some.fn = Some.prototype = { init: function (val) { if (val == null) { @@ -41,6 +51,9 @@ map: function (fn) { return new Some(fn(this.val)) + }, + map2: function (maybeB) { + }, isSome: trueFunction, isJust: trueFunction, @@ -78,7 +91,7 @@ return new Some(this) } - var None = Nothing = Maybe.Nothing = Maybe.None = Maybe.none = Maybe.nothing = function () { + var None = Nothing = Maybe.Nothing = Maybe.None = Maybe.none = Maybe.nothing = window.None = function () { return new None.fn.init() }; @@ -345,11 +358,15 @@ } var sequenceMaybe = function (list) { - return list.head.map(List).flatMap(function (list1) { - return sequenceMaybe(list.tail) + return list.foldRight(Some(Nil))(function(a,b) { + return Maybe.map2(a,b)(cons) }) } + var cons = function(head, tail) { + return tail.cons(head) + } + List.fn = List.prototype = { init: function (head, tail) { @@ -379,6 +396,12 @@ return foldLeft(fn, initialValue, self) } }, + foldRight: function (initialValue) { + var self = this + return function (fn) { + return foldRight(fn, self, initialValue) + } + }, append: function (list2) { return append(this, list2) }, diff --git a/src/test/javascript/list_spec.js b/src/test/javascript/list_spec.js index f6090d5..77bcf5c 100644 --- a/src/test/javascript/list_spec.js +++ b/src/test/javascript/list_spec.js @@ -72,12 +72,15 @@ describe("An immutable list", function () { describe("will sequence a list", function () { describe("of Maybes", function () { -// it("with one defined element", function () { -// expect(List(Some("hello")).sequenceMaybe().some().toArray()).toEqual(["hello"]) -// }) -// it("with multiple defined elements", function() { -// expect(List(Some(1),Some(2),Some(3)).sequenceMaybe().some().toArray()).toBeSomeMaybeWithList([1,2,3]) -// }) + it("with one defined element", function () { + expect(List(Some("hello"), Nil).sequenceMaybe().some().toArray()).toEqual(["hello"]) + }) + it("with multiple defined elements", function() { + expect([Some(1),Some(2),Some(3)].list().sequenceMaybe().some().toArray()).toEqual([1,2,3]) + }) + it("with multiple defined elements and one undefined element", function() { + expect([Some(1),Some(2),None()].list().sequenceMaybe()).toBeNoneMaybe() + }) }) }) }) \ No newline at end of file From 494a294c88c744e715653919388dcdd4f346bfb0 Mon Sep 17 00:00:00 2001 From: Chris Myers Date: Fri, 4 Oct 2013 20:41:15 +0100 Subject: [PATCH 08/12] Cleaned up map2 --- src/main/javascript/monad.js | 13 +++++++------ src/test/javascript/list_spec.js | 3 +++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/javascript/monad.js b/src/main/javascript/monad.js index 7cdabd3..29204ba 100644 --- a/src/main/javascript/monad.js +++ b/src/main/javascript/monad.js @@ -19,6 +19,9 @@ }; + + + /* Maybe Monad */ var Maybe = window.Maybe = {} @@ -31,8 +34,8 @@ return new Some.fn.init(val) }; - Maybe.map2 = function (maybeA, maybeB) { - return function (fn) { + Maybe.map2 = function (fn) { + return function (maybeA, maybeB) { return maybeA.flatMap(function (a) { return maybeB.map(function (b) { return fn(a, b) @@ -358,12 +361,10 @@ } var sequenceMaybe = function (list) { - return list.foldRight(Some(Nil))(function(a,b) { - return Maybe.map2(a,b)(cons) - }) + return list.foldRight(Some(Nil))(Maybe.map2(cons)) } - var cons = function(head, tail) { + var cons = function (head, tail) { return tail.cons(head) } diff --git a/src/test/javascript/list_spec.js b/src/test/javascript/list_spec.js index 77bcf5c..5a72916 100644 --- a/src/test/javascript/list_spec.js +++ b/src/test/javascript/list_spec.js @@ -81,6 +81,9 @@ describe("An immutable list", function () { it("with multiple defined elements and one undefined element", function() { expect([Some(1),Some(2),None()].list().sequenceMaybe()).toBeNoneMaybe() }) + it("with no elements", function() { + expect([].list().sequenceMaybe().some().toArray()).toEqual([]) + }) }) }) }) \ No newline at end of file From d8fb6607e875f73a3f06983019ccc4bade929920 Mon Sep 17 00:00:00 2001 From: Chris Myers Date: Fri, 4 Oct 2013 23:46:21 +0100 Subject: [PATCH 09/12] Implemented currying, the awesome way --- example/IO.html | 15 +- example/List.html | 10 +- example/wu-0.1.8.min.js | 16 - src/main/javascript/functional.js | 824 ------------------ src/main/javascript/monad.js | 27 +- src/test/javascript/maybe_spec.js | 3 +- src/test/javascript/monad_transformer_spec.js | 4 +- src/test/javascript/validation_spec.js | 12 +- 8 files changed, 40 insertions(+), 871 deletions(-) delete mode 100644 example/wu-0.1.8.min.js delete mode 100644 src/main/javascript/functional.js diff --git a/example/IO.html b/example/IO.html index 4a670ae..1af5cae 100644 --- a/example/IO.html +++ b/example/IO.html @@ -4,7 +4,6 @@ - Example IO page @@ -23,22 +22,18 @@ - Example IO page @@ -23,9 +22,6 @@