Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added "do notation" for Maybe, Either, Validation and IO Monads using generators #159

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = function (config) {
colors: true,
logLevel: config.LOG_INFO,
autoWatch: false,
browsers: ['PhantomJS'],
browsers: ['ChromeHeadless'],
singleRun: true,
concurrency: 6e6
});
Expand Down
18 changes: 18 additions & 0 deletions src/monet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -633,6 +643,8 @@
return maybe.toList()
}

Maybe.do = doNotation.bind(null, Maybe)

Maybe.fn = Maybe.prototype = {
init: function (isValue, val) {
this.isValue = isValue
Expand Down Expand Up @@ -757,6 +769,8 @@
return Success(v)
}

Validation.do = doNotation.bind(null, Validation)

Validation.fn = Validation.prototype = {
init: function (val, success) {
this.val = val
Expand Down Expand Up @@ -918,6 +932,8 @@
})
}

IO.do = doNotation.bind(null, IO)

IO.fn = IO.prototype = {
init: function (effectFn) {
if (!isFunction(effectFn)) {
Expand Down Expand Up @@ -973,6 +989,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
Expand Down
47 changes: 47 additions & 0 deletions test/either-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
})

})
24 changes: 24 additions & 0 deletions test/io-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
})
})
45 changes: 45 additions & 0 deletions test/maybe-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
return a
})
})

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).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()
})
})
})
47 changes: 47 additions & 0 deletions test/validation-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
})

})