From fa12cb36bbd7f2404347fbee226c234bc415f5f2 Mon Sep 17 00:00:00 2001 From: Andras Sevcsik Date: Tue, 27 Feb 2018 00:21:57 +0100 Subject: [PATCH 1/6] Added first version of unit tests for Maybe.do --- test/maybe-spec.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/maybe-spec.js b/test/maybe-spec.js index dde4fba..60dae5e 100644 --- a/test/maybe-spec.js +++ b/test/maybe-spec.js @@ -449,5 +449,50 @@ describe('A Maybe', function () { }) }) + describe('do notation', function() { + it('will yield the Just value inside a generator', function () { + Maybe.do(function*() { + var a = yield Maybe.Just(5) + expect(a).toBe(5) + }) + }) + + it('will return the value wrapped in Just', function() { + var result = Maybe.do(function* () { + var a = yield Maybe.Just(5) + var b = yield Maybe.Just(1) + return a + b + }) + + expect(result.isJust()).toBe(true) + expect(result.toBeSomeMaybeWith(6)) + }) + + it('will return Nothing when Nothing is yielded inside the generator', function() { + var result = Maybe.do(function* () { + var a = yield Maybe.Just(5) + var b = yield Maybe.Nothing() + return a + b + }) + expect(result.toBeNoneMaybe) + }) + + it('will short-circuit the generator when Nothing is yielded', function() { + var spyBeforeNothing = jasmine.createSpy() + var spyAfterNothing = jasmine.createSpy() + + var result = Maybe.do(function* () { + spyBeforeNothing() + var a = yield Maybe.Nothing() + spyAfterNothing() + var b = yield Maybe.Just(3) + return a + b + }) + + expect(spyBeforeNothing).toHaveBeenCalled() + expect(spyAfterNothing).not.toHaveBeenCalled() + expect(result).toBeNoneMaybe() + }) + }) }) From bba05c46ec8bd4bba6ab8e7969ca3389f1b84e49 Mon Sep 17 00:00:00 2001 From: Andras Sevcsik Date: Tue, 27 Feb 2018 00:59:51 +0100 Subject: [PATCH 2/6] Added do notation implementation --- src/monet.js | 12 ++++++++++++ test/maybe-spec.js | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/monet.js b/src/monet.js index d391f5b..e9d9e32 100644 --- a/src/monet.js +++ b/src/monet.js @@ -65,6 +65,16 @@ Free: 'Free' } + function doNotation(monad, co) { + function iterate (el) { + return el.done ? monad.unit(el.value) : el.value.bind(function (val) { + return iterate(it.next(val)) + }) + } + var it = co() + return iterate(it.next()) + } + function setType(target, typeName) { target[TYPE_KEY] = 'Monet/' + typeName } @@ -633,6 +643,8 @@ return maybe.toList() } + Maybe.do = doNotation.bind(null, Maybe) + Maybe.fn = Maybe.prototype = { init: function (isValue, val) { this.isValue = isValue diff --git a/test/maybe-spec.js b/test/maybe-spec.js index 60dae5e..beea23e 100644 --- a/test/maybe-spec.js +++ b/test/maybe-spec.js @@ -454,6 +454,7 @@ describe('A Maybe', function () { Maybe.do(function*() { var a = yield Maybe.Just(5) expect(a).toBe(5) + return a }) }) @@ -464,8 +465,7 @@ describe('A Maybe', function () { return a + b }) - expect(result.isJust()).toBe(true) - expect(result.toBeSomeMaybeWith(6)) + expect(result).toBeSomeMaybeWith(6) }) it('will return Nothing when Nothing is yielded inside the generator', function() { @@ -475,7 +475,7 @@ describe('A Maybe', function () { return a + b }) - expect(result.toBeNoneMaybe) + expect(result).toBeNoneMaybe() }) it('will short-circuit the generator when Nothing is yielded', function() { From 0599c5c689e30800ba34131eebfa2fdc86f52561 Mon Sep 17 00:00:00 2001 From: Andras Sevcsik Date: Mon, 5 Mar 2018 01:08:03 +0100 Subject: [PATCH 3/6] Added do to Either type --- src/monet.js | 2 ++ test/either-spec.js | 47 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/monet.js b/src/monet.js index e9d9e32..ec14b14 100644 --- a/src/monet.js +++ b/src/monet.js @@ -985,6 +985,8 @@ return new Either.fn.init(val, false) } + Either.do = doNotation.bind(null, Either) + Either.fn = Either.prototype = { init: function (val, isRightValue) { this.isRightValue = isRightValue diff --git a/test/either-spec.js b/test/either-spec.js index 632514b..c1b19ff 100644 --- a/test/either-spec.js +++ b/test/either-spec.js @@ -293,4 +293,51 @@ describe('An Either', function () { }) + describe('do notation', function() { + it('will yield the Right value inside a generator', function () { + Either.do(function*() { + var a = yield Either.Right(5) + expect(a).toBe(5) + return a + }) + }) + + it('will return the value wrapped in Right', function() { + var result = Either.do(function* () { + var a = yield Either.Right(5) + var b = yield Either.Right(1) + return a + b + }) + + expect(result).toBeRightWith(6) + }) + + it('will return Left when Left is yielded inside the generator', function() { + var result = Either.do(function* () { + var a = yield Either.Right(5) + var b = yield Either.Left(10) + return a + b + }) + + expect(result).toBeLeftWith(10) + }) + + it('will short-circuit the generator when Left is yielded', function() { + var spyBeforeLeft = jasmine.createSpy() + var spyAfterLeft = jasmine.createSpy() + + var result = Either.do(function* () { + spyBeforeLeft() + var a = yield Either.Left(5) + spyAfterLeft() + var b = yield Either.Right(3) + return a + b + }) + + expect(spyBeforeLeft).toHaveBeenCalled() + expect(spyAfterLeft).not.toHaveBeenCalled() + expect(result).toBeLeftWith(5) + }) + }) + }) From 283b3058e0be462c3041403ce3fc766f0a863891 Mon Sep 17 00:00:00 2001 From: Andras Sevcsik Date: Mon, 5 Mar 2018 01:47:42 +0100 Subject: [PATCH 4/6] Added do notation to IO monad --- src/monet.js | 2 ++ test/io-spec.js | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/monet.js b/src/monet.js index ec14b14..182b7ec 100644 --- a/src/monet.js +++ b/src/monet.js @@ -930,6 +930,8 @@ }) } + IO.do = doNotation.bind(null, IO) + IO.fn = IO.prototype = { init: function (effectFn) { if (!isFunction(effectFn)) { diff --git a/test/io-spec.js b/test/io-spec.js index 4ef116d..93247dd 100644 --- a/test/io-spec.js +++ b/test/io-spec.js @@ -51,4 +51,28 @@ describe('An IO monad', function () { expect(effect.ap).toBe(effect['fantasy-land/ap']) }) }) + + describe('do notation', function() { + it('will yield the Success value inside a generator', function () { + var effect = IO.do(function* () { + var a = yield IO.of(5) + expect(a).toBe(5) + return a + }) + + effect.run() + }) + + it('will return the value wrapped IO', function() { + var effect = IO.do(function* () { + var a = yield IO.of(5) + var b = yield IO.of(1) + return a + b + }) + + effect.map(function (val) { + expect(val).toBe(6) + }).run() + }) + }) }) From 318d320bd073ddd0963b46a1afffe2f261bc159b Mon Sep 17 00:00:00 2001 From: Andras Sevcsik Date: Mon, 5 Mar 2018 01:50:15 +0100 Subject: [PATCH 5/6] Added do notation to Validation monad --- src/monet.js | 2 ++ test/validation-spec.js | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/monet.js b/src/monet.js index 182b7ec..c9c359a 100644 --- a/src/monet.js +++ b/src/monet.js @@ -769,6 +769,8 @@ return Success(v) } + Validation.do = doNotation.bind(null, Validation) + Validation.fn = Validation.prototype = { init: function (val, success) { this.val = val diff --git a/test/validation-spec.js b/test/validation-spec.js index f1429ba..095a455 100644 --- a/test/validation-spec.js +++ b/test/validation-spec.js @@ -281,4 +281,51 @@ describe('A Validation', function () { }) + describe('do notation', function() { + it('will yield the Success value inside a generator', function () { + Validation.do(function*() { + var a = yield Validation.Success(5) + expect(a).toBe(5) + return a + }) + }) + + it('will return the value wrapped in Success', function() { + var result = Validation.do(function* () { + var a = yield Validation.Success(5) + var b = yield Validation.Success(1) + return a + b + }) + + expect(result).toBeSuccessWith(6) + }) + + it('will return Fail when Left is yielded inside the generator', function() { + var result = Validation.do(function* () { + var a = yield Validation.Success(5) + var b = yield Validation.Fail(10) + return a + b + }) + + expect(result).toBeFailureWith(10) + }) + + it('will short-circuit the generator when Fail is yielded', function() { + var spyBeforeFail = jasmine.createSpy() + var spyAfterFail = jasmine.createSpy() + + var result = Validation.do(function* () { + spyBeforeFail() + var a = yield Validation.Fail(5) + spyAfterFail() + var b = yield Validation.Success(3) + return a + b + }) + + expect(spyBeforeFail).toHaveBeenCalled() + expect(spyAfterFail).not.toHaveBeenCalled() + expect(result).toBeFailureWith(5) + }) + }) + }) From 9c6d65e4d34a2de0d6a2c2125eba55e88c5cf080 Mon Sep 17 00:00:00 2001 From: Andras Sevcsik Date: Mon, 5 Mar 2018 04:31:57 +0100 Subject: [PATCH 6/6] Changed PhantomJS to ChromeHeadless because PhantomJS doesn't support generator functions used in new unit tests --- karma.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/karma.conf.js b/karma.conf.js index e131253..e119fa4 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -14,7 +14,7 @@ module.exports = function (config) { colors: true, logLevel: config.LOG_INFO, autoWatch: false, - browsers: ['PhantomJS'], + browsers: ['ChromeHeadless'], singleRun: true, concurrency: 6e6 });