From d894264e10b051f4caa1c506ee3f16fe2a852132 Mon Sep 17 00:00:00 2001
From: Patrick Michalina
Date: Wed, 9 Jan 2019 16:54:28 -0600
Subject: [PATCH 1/8] feat: introduce result monad
---
src/monads/index.ts | 9 +++---
src/monads/result.ts | 54 +++++++++++++++++++++++++++++++++++
test/interfaces/index.spec.ts | 5 ++--
test/monads/result.spec.ts | 45 +++++++++++++++++++++++++++++
4 files changed, 106 insertions(+), 7 deletions(-)
create mode 100644 src/monads/result.ts
create mode 100644 test/monads/result.spec.ts
diff --git a/src/monads/index.ts b/src/monads/index.ts
index 975cd4a..9f55089 100644
--- a/src/monads/index.ts
+++ b/src/monads/index.ts
@@ -1,4 +1,5 @@
-export { monad } from './monad'
-export { maybe } from './maybe'
-export { either } from './either'
-export { reader } from './reader'
\ No newline at end of file
+export * from './monad'
+export * from './maybe'
+export * from './either'
+export * from './reader'
+export * from './result'
\ No newline at end of file
diff --git a/src/monads/result.ts b/src/monads/result.ts
new file mode 100644
index 0000000..6f232e1
--- /dev/null
+++ b/src/monads/result.ts
@@ -0,0 +1,54 @@
+import { IMaybe } from "../interfaces"
+import { maybe } from "./maybe"
+
+const returnTrue = () => true
+const returnFalse = () => false
+
+export interface IResult {
+ isOk(): boolean
+ isFail(): boolean
+ maybeOk(): IMaybe
+ maybeFail(): IMaybe
+ unwrap(): T | never
+ unwrapOr(opt: T): T
+ unwrapFail(): E | never
+ // map(fn: (val: T) => M): IResult
+ // match(fn: Match): U
+ // map_err(fn: (err: E) => U): Result
+ // and_then(fn: (val: T) => Result): Result
+}
+
+export interface IResultOk extends IResult {
+ unwrap(): T
+ unwrapOr(opt: T): T
+ unwrapFail(): never
+}
+export interface IResultFail extends IResult {
+ unwrap(): never
+ unwrapOr(opt: T): T
+ unwrapFail(): E
+}
+
+export const ok = (val: T): IResultOk => {
+ return {
+ isOk: returnTrue,
+ isFail: returnFalse,
+ maybeOk: () => maybe(val),
+ maybeFail: maybe,
+ unwrap: () => val,
+ unwrapOr: _ => val,
+ unwrapFail: () => { throw new Error() }
+ }
+}
+
+export const fail = (err: E): IResultFail => {
+ return {
+ isOk: returnTrue,
+ isFail: returnFalse,
+ maybeOk: maybe,
+ maybeFail: () => maybe(err),
+ unwrap: () => { throw new Error('Cannot unwrap a failure') },
+ unwrapOr: opt => opt,
+ unwrapFail: () => err
+ }
+}
diff --git a/test/interfaces/index.spec.ts b/test/interfaces/index.spec.ts
index 2a9c05e..2e26f43 100644
--- a/test/interfaces/index.spec.ts
+++ b/test/interfaces/index.spec.ts
@@ -1,8 +1,7 @@
import '../../src/interfaces'
-describe('', () => {
- it('should', () => {
+describe('Interfaces', () => {
+ it('should cover interfaces', () => {
// stubbed just to get coverage up from interfaces
- // which are not used
})
})
\ No newline at end of file
diff --git a/test/monads/result.spec.ts b/test/monads/result.spec.ts
new file mode 100644
index 0000000..ca79ec7
--- /dev/null
+++ b/test/monads/result.spec.ts
@@ -0,0 +1,45 @@
+import { ok } from '../../src/monads'
+
+describe('result', () => {
+ describe('ok', () => {
+ it('should return true when "isOk" invoked on a success path', () => {
+ expect(ok(1).isOk()).toEqual(true)
+ })
+
+ it('should return false when "isFail" invoked on a success path', () => {
+ expect(ok(1).isFail()).toEqual(false)
+ })
+
+ it('should unwrap', () => {
+ expect(ok(1).unwrap()).toEqual(1)
+ expect(ok("Test").unwrap()).toEqual("Test")
+ })
+
+ it('should return proper value when "unwrapOr" is applied', () => {
+ expect(ok(1).unwrapOr(25)).toEqual(1)
+ expect(ok("Test").unwrapOr("Some Other")).toEqual("Test")
+ })
+
+ it('should throw an exception whe "unwrapOrFail" called on an ok value', () => {
+ expect(() => {
+ ok(1).unwrapFail()
+ }).toThrowError()
+ })
+
+ it('should ...', () => {
+ const _sut = ok('Test')
+ .maybeOk()
+ .valueOr('Some Other')
+
+ expect(_sut).toEqual('Test')
+ })
+
+ it('should ...', () => {
+ const _sut = ok('Test')
+ .maybeFail()
+ .valueOrUndefined()
+
+ expect(_sut).toEqual(undefined)
+ })
+ })
+})
From 337ff103b75583fc1af13bc5e097209ced11499a Mon Sep 17 00:00:00 2001
From: Patrick Michalina
Date: Wed, 9 Jan 2019 20:15:59 -0600
Subject: [PATCH 2/8] tests
---
src/monads/result.ts | 19 +++++++++-------
test/monads/result.spec.ts | 44 +++++++++++++++++++++++++++++++++++---
2 files changed, 52 insertions(+), 11 deletions(-)
diff --git a/src/monads/result.ts b/src/monads/result.ts
index 6f232e1..1ec5688 100644
--- a/src/monads/result.ts
+++ b/src/monads/result.ts
@@ -3,6 +3,9 @@ import { maybe } from "./maybe"
const returnTrue = () => true
const returnFalse = () => false
+const returnValue = (val: T) => () => val
+const returnMaybe = (val: T) => () => maybe(val)
+const throwError = (message: string) => () => { throw new Error(message) }
export interface IResult {
isOk(): boolean
@@ -33,22 +36,22 @@ export const ok = (val: T): IResultOk => {
return {
isOk: returnTrue,
isFail: returnFalse,
- maybeOk: () => maybe(val),
+ maybeOk: returnMaybe(val),
maybeFail: maybe,
- unwrap: () => val,
+ unwrap: returnValue(val),
unwrapOr: _ => val,
- unwrapFail: () => { throw new Error() }
+ unwrapFail: throwError('Cannot unwrap a success')
}
}
export const fail = (err: E): IResultFail => {
return {
- isOk: returnTrue,
- isFail: returnFalse,
+ isOk: returnFalse,
+ isFail: returnTrue,
maybeOk: maybe,
- maybeFail: () => maybe(err),
- unwrap: () => { throw new Error('Cannot unwrap a failure') },
+ maybeFail: returnMaybe(err),
+ unwrap: throwError('Cannot unwrap a failure'),
unwrapOr: opt => opt,
- unwrapFail: () => err
+ unwrapFail: returnValue(err)
}
}
diff --git a/test/monads/result.spec.ts b/test/monads/result.spec.ts
index ca79ec7..438bc71 100644
--- a/test/monads/result.spec.ts
+++ b/test/monads/result.spec.ts
@@ -1,4 +1,4 @@
-import { ok } from '../../src/monads'
+import { ok, fail } from '../../src/monads'
describe('result', () => {
describe('ok', () => {
@@ -30,7 +30,7 @@ describe('result', () => {
const _sut = ok('Test')
.maybeOk()
.valueOr('Some Other')
-
+
expect(_sut).toEqual('Test')
})
@@ -38,8 +38,46 @@ describe('result', () => {
const _sut = ok('Test')
.maybeFail()
.valueOrUndefined()
-
+
expect(_sut).toEqual(undefined)
})
})
+
+ describe('fail', () => {
+ it('should return false when "isOk" invoked', () => {
+ expect(fail(1).isOk()).toEqual(false)
+ })
+
+ it('should return true when "isFail" invoked', () => {
+ expect(fail(1).isFail()).toEqual(true)
+ })
+
+ it('should return empty maybe when "maybeOk" is invoked', () => {
+ const _sut = fail('Test')
+ .maybeOk()
+ .valueOr('Some Other1')
+
+ expect(_sut).toEqual('Some Other1')
+ })
+
+ it('should return fail object when "maybeFail" is invoked', () => {
+ const _sut = fail('Test')
+ .maybeFail()
+ .valueOr('Some Other2')
+
+ expect(_sut).toEqual('Test')
+ })
+
+ it('should throw an exception on "unwrap"', () => {
+ expect(() => { fail(1).unwrap() }).toThrowError()
+ })
+
+ it('should return fail object on "unwrapFail"', () => {
+ expect(fail('123').unwrapFail()).toEqual('123')
+ })
+
+ it('should return input object on "unwrapOr"', () => {
+ expect(fail('123').unwrapOr('456')).toEqual('456')
+ })
+ })
})
From 3e410cd0720e7a8ac7c37fab6defba1d914f4be7 Mon Sep 17 00:00:00 2001
From: Patrick Michalina
Date: Wed, 9 Jan 2019 20:17:16 -0600
Subject: [PATCH 3/8] readme
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index c2fba68..bd8a39b 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ npm install typescript-monads
-
+
```
From 51016ef10bde29b5a0caaf9aaed9c58cd6e44c84 Mon Sep 17 00:00:00 2001
From: Patrick Michalina
Date: Wed, 9 Jan 2019 20:59:15 -0600
Subject: [PATCH 4/8] wip
---
README.md | 3 +--
src/monads/result.ts | 26 +++++++++++++++++++++++---
test/monads/result.spec.ts | 14 +++++++++++++-
3 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index bd8a39b..ff45a20 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@
-**typescript-monads** helps you write safer code by using abstractions over dubious program state and control flow.
+**typescript-monads** helps you write safer code by using abstractions over messy control flow and state.
# Getting Started
@@ -92,7 +92,6 @@ maybe(process.env.DB_URL)
})
```
-
# Either
TODO
diff --git a/src/monads/result.ts b/src/monads/result.ts
index 1ec5688..1518084 100644
--- a/src/monads/result.ts
+++ b/src/monads/result.ts
@@ -5,7 +5,8 @@ const returnTrue = () => true
const returnFalse = () => false
const returnValue = (val: T) => () => val
const returnMaybe = (val: T) => () => maybe(val)
-const throwError = (message: string) => () => { throw new Error(message) }
+const throwReferenceError = (message: string) => () => { throw new ReferenceError(message) }
+type Predicate = () => boolean
export interface IResult {
isOk(): boolean
@@ -40,7 +41,7 @@ export const ok = (val: T): IResultOk => {
maybeFail: maybe,
unwrap: returnValue(val),
unwrapOr: _ => val,
- unwrapFail: throwError('Cannot unwrap a success')
+ unwrapFail: throwReferenceError('Cannot unwrap a success')
}
}
@@ -50,8 +51,27 @@ export const fail = (err: E): IResultFail => {
isFail: returnTrue,
maybeOk: maybe,
maybeFail: returnMaybe(err),
- unwrap: throwError('Cannot unwrap a failure'),
+ unwrap: throwReferenceError('Cannot unwrap a failure'),
unwrapOr: opt => opt,
unwrapFail: returnValue(err)
}
}
+
+/**
+ * Utility function to quickly create ok/fail pairs.
+ */
+export const result = (predicate: Predicate, okValue: T, failValue: E): IResult =>
+ predicate()
+ ? ok(okValue)
+ : fail(failValue)
+
+// /**
+// * Utility function to quickly create ok/fail pairs in curried form.
+// */
+// export const curriedResult =
+// (predicate: Predicate) =>
+// (okValue: T) =>
+// (failValue: E): IResult =>
+// predicate()
+// ? ok(okValue)
+// : fail(failValue)
\ No newline at end of file
diff --git a/test/monads/result.spec.ts b/test/monads/result.spec.ts
index 438bc71..344d4df 100644
--- a/test/monads/result.spec.ts
+++ b/test/monads/result.spec.ts
@@ -1,4 +1,4 @@
-import { ok, fail } from '../../src/monads'
+import { ok, fail, result } from '../../src/monads'
describe('result', () => {
describe('ok', () => {
@@ -80,4 +80,16 @@ describe('result', () => {
expect(fail('123').unwrapOr('456')).toEqual('456')
})
})
+
+ describe('result', () => {
+ it('should return failure when predicate yields false', () => {
+ const sut = result(() => 1 + 1 === 3, true, 'FAILURE!')
+ expect(sut.isFail()).toEqual(true)
+ })
+
+ it('should return ok when predicate yields true', () => {
+ const sut = result(() => 1 + 1 === 2, true, 'FAILURE!')
+ expect(sut.isOk()).toEqual(true)
+ })
+ })
})
From e97c8282d0ea721ff3b2e8b1b2aa39bca1a70559 Mon Sep 17 00:00:00 2001
From: Patrick Michalina
Date: Wed, 9 Jan 2019 22:10:14 -0600
Subject: [PATCH 5/8] lint
---
src/interfaces/maybe.interface.ts | 2 +-
src/monads/either.ts | 2 +-
src/monads/maybe.ts | 2 +-
src/monads/monad.ts | 2 +-
src/monads/reader.ts | 2 +-
src/monads/result.ts | 36 +++++++++++++++----------------
src/util/maybe-env.ts | 2 +-
test/monads/result.spec.ts | 32 +++++++++++++++++++++++++--
tslint.json | 3 ++-
9 files changed, 55 insertions(+), 28 deletions(-)
diff --git a/src/interfaces/maybe.interface.ts b/src/interfaces/maybe.interface.ts
index 5d0c034..fb726f5 100644
--- a/src/interfaces/maybe.interface.ts
+++ b/src/interfaces/maybe.interface.ts
@@ -1,4 +1,4 @@
-import { IMonad } from "./monad.interface"
+import { IMonad } from './monad.interface'
/**
* Define a contract to unwrap Maybe object
diff --git a/src/monads/either.ts b/src/monads/either.ts
index 955f223..a880c1e 100644
--- a/src/monads/either.ts
+++ b/src/monads/either.ts
@@ -1,4 +1,4 @@
-import { IEither, IEitherPattern } from "../interfaces"
+import { IEither, IEitherPattern } from '../interfaces'
const exists = (t: T) => t !== null && t !== undefined
const bothExist = (left?: L) => (right?: R) => exists(left) && exists(right)
diff --git a/src/monads/maybe.ts b/src/monads/maybe.ts
index afa58f9..16741eb 100644
--- a/src/monads/maybe.ts
+++ b/src/monads/maybe.ts
@@ -1,4 +1,4 @@
-import { IMaybe, IMaybePattern } from "../interfaces"
+import { IMaybe, IMaybePattern } from '../interfaces'
const isEmpty = (value: T) => value === null || value === undefined
const isNotEmpty = (value: T) => !isEmpty(value)
diff --git a/src/monads/monad.ts b/src/monads/monad.ts
index 38829cc..0515304 100644
--- a/src/monads/monad.ts
+++ b/src/monads/monad.ts
@@ -1,4 +1,4 @@
-import { mapping, IMonad } from "../interfaces"
+import { mapping, IMonad } from '../interfaces'
// tslint:disable:readonly-array
export const monad = (x: T, ...args: any[]): IMonad => {
diff --git a/src/monads/reader.ts b/src/monads/reader.ts
index 3f64374..f0cfea7 100644
--- a/src/monads/reader.ts
+++ b/src/monads/reader.ts
@@ -1,4 +1,4 @@
-import { IReader } from "../interfaces"
+import { IReader } from '../interfaces'
// tslint:disable:no-this
export const reader = (fn: (config: E) => A): IReader => {
diff --git a/src/monads/result.ts b/src/monads/result.ts
index 1518084..d8aa8e3 100644
--- a/src/monads/result.ts
+++ b/src/monads/result.ts
@@ -1,11 +1,12 @@
-import { IMaybe } from "../interfaces"
-import { maybe } from "./maybe"
+import { IMaybe } from '../interfaces'
+import { maybe } from './maybe'
const returnTrue = () => true
const returnFalse = () => false
const returnValue = (val: T) => () => val
const returnMaybe = (val: T) => () => maybe(val)
const throwReferenceError = (message: string) => () => { throw new ReferenceError(message) }
+
type Predicate = () => boolean
export interface IResult {
@@ -16,21 +17,25 @@ export interface IResult {
unwrap(): T | never
unwrapOr(opt: T): T
unwrapFail(): E | never
- // map(fn: (val: T) => M): IResult
+ map(fn: (val: T) => M): IResult
+ mapFail(fn: (err: E) => M): IResult
// match(fn: Match): U
- // map_err(fn: (err: E) => U): Result
// and_then(fn: (val: T) => Result): Result
}
export interface IResultOk extends IResult {
unwrap(): T
unwrapOr(opt: T): T
- unwrapFail(): never
+ unwrapFail(): never,
+ map(fn: (val: T) => M): IResultOk
+ mapFail(fn: (err: E) => M): IResultOk
}
export interface IResultFail extends IResult {
unwrap(): never
unwrapOr(opt: T): T
- unwrapFail(): E
+ unwrapFail(): E,
+ map(fn: (val: T) => M): IResultFail
+ mapFail(fn: (err: E) => M): IResultFail
}
export const ok = (val: T): IResultOk => {
@@ -41,7 +46,9 @@ export const ok = (val: T): IResultOk => {
maybeFail: maybe,
unwrap: returnValue(val),
unwrapOr: _ => val,
- unwrapFail: throwReferenceError('Cannot unwrap a success')
+ unwrapFail: throwReferenceError('Cannot unwrap a success'),
+ map: (fn: (val: T) => M) => ok(fn(val)),
+ mapFail: (_: (err: E) => M) => ok(val)
}
}
@@ -53,7 +60,9 @@ export const fail = (err: E): IResultFail => {
maybeFail: returnMaybe(err),
unwrap: throwReferenceError('Cannot unwrap a failure'),
unwrapOr: opt => opt,
- unwrapFail: returnValue(err)
+ unwrapFail: returnValue(err),
+ map: (_: (val: T) => M) => fail(err),
+ mapFail: (fn: (err: E) => M) => fail(fn(err))
}
}
@@ -64,14 +73,3 @@ export const result = (predicate: Predicate, okValue: T, failValue: E): IR
predicate()
? ok(okValue)
: fail(failValue)
-
-// /**
-// * Utility function to quickly create ok/fail pairs in curried form.
-// */
-// export const curriedResult =
-// (predicate: Predicate) =>
-// (okValue: T) =>
-// (failValue: E): IResult =>
-// predicate()
-// ? ok(okValue)
-// : fail(failValue)
\ No newline at end of file
diff --git a/src/util/maybe-env.ts b/src/util/maybe-env.ts
index 5f634f7..a1c22e2 100644
--- a/src/util/maybe-env.ts
+++ b/src/util/maybe-env.ts
@@ -1,4 +1,4 @@
-import { reader, maybe } from ".."
+import { reader, maybe } from '..'
export interface GetFromEnvironmentReader {
readEnv(key: string): string | undefined
diff --git a/test/monads/result.spec.ts b/test/monads/result.spec.ts
index 344d4df..3159b4f 100644
--- a/test/monads/result.spec.ts
+++ b/test/monads/result.spec.ts
@@ -12,12 +12,12 @@ describe('result', () => {
it('should unwrap', () => {
expect(ok(1).unwrap()).toEqual(1)
- expect(ok("Test").unwrap()).toEqual("Test")
+ expect(ok('Test').unwrap()).toEqual('Test')
})
it('should return proper value when "unwrapOr" is applied', () => {
expect(ok(1).unwrapOr(25)).toEqual(1)
- expect(ok("Test").unwrapOr("Some Other")).toEqual("Test")
+ expect(ok('Test').unwrapOr('Some Other')).toEqual('Test')
})
it('should throw an exception whe "unwrapOrFail" called on an ok value', () => {
@@ -41,6 +41,20 @@ describe('result', () => {
expect(_sut).toEqual(undefined)
})
+
+ it('should map function', () => {
+ const sut = ok(1)
+ .map(b => b.toString())
+ .unwrap()
+ expect(sut).toEqual('1')
+ })
+
+ it('should not mapFail', () => {
+ const sut = ok(1)
+ .mapFail(b => '')
+ .unwrap()
+ expect(sut).toEqual(1)
+ })
})
describe('fail', () => {
@@ -79,6 +93,20 @@ describe('result', () => {
it('should return input object on "unwrapOr"', () => {
expect(fail('123').unwrapOr('456')).toEqual('456')
})
+
+ it('should not map', () => {
+ const sut = fail(1)
+ .map(b => b.toString())
+ .unwrapFail()
+ expect(sut).toEqual(1)
+ })
+
+ it('should mapFail', () => {
+ const sut = fail(1)
+ .mapFail(b => b.toString())
+ .unwrapFail()
+ expect(sut).toEqual('1')
+ })
})
describe('result', () => {
diff --git a/tslint.json b/tslint.json
index ecf3f72..18a8d24 100644
--- a/tslint.json
+++ b/tslint.json
@@ -25,6 +25,7 @@
"no-this": true,
"no-class": true,
"no-expression-statement": false,
- "no-if-statement": true
+ "no-if-statement": true,
+ "quotemark": [true, "single"]
}
}
\ No newline at end of file
From c0aa9f6eb98fab21301d326cb30ecbc6cab073ef Mon Sep 17 00:00:00 2001
From: Patrick Michalina
Date: Wed, 9 Jan 2019 22:24:43 -0600
Subject: [PATCH 6/8] flatMap
---
README.md | 5 +++--
src/monads/result.ts | 10 +++++++---
test/monads/result.spec.ts | 16 ++++++++++++++++
3 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index ff45a20..578c2aa 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,8 @@
**typescript-monads** helps you write safer code by using abstractions over messy control flow and state.
-# Getting Started
+# Installation
+You can use this library in the browser, node, or a bundler
## Node or as a module
```bash
@@ -54,7 +55,7 @@ var someRemoteValue;
typescriptMonads.maybe(someRemoteValue).tapSome(console.log)
```
-# Usage
+# Example Usage
* [Maybe](#maybe)
* [Either](#either)
diff --git a/src/monads/result.ts b/src/monads/result.ts
index d8aa8e3..e8ec296 100644
--- a/src/monads/result.ts
+++ b/src/monads/result.ts
@@ -19,8 +19,8 @@ export interface IResult {
unwrapFail(): E | never
map(fn: (val: T) => M): IResult
mapFail(fn: (err: E) => M): IResult
+ flatMap(fn: (val: T) => IResult): IResult
// match(fn: Match): U
- // and_then(fn: (val: T) => Result): Result
}
export interface IResultOk extends IResult {
@@ -30,12 +30,14 @@ export interface IResultOk extends IResult {
map(fn: (val: T) => M): IResultOk
mapFail(fn: (err: E) => M): IResultOk
}
+
export interface IResultFail extends IResult {
unwrap(): never
unwrapOr(opt: T): T
unwrapFail(): E,
map(fn: (val: T) => M): IResultFail
mapFail(fn: (err: E) => M): IResultFail
+ flatMap(fn: (val: T) => IResult): IResultFail
}
export const ok = (val: T): IResultOk => {
@@ -48,7 +50,8 @@ export const ok = (val: T): IResultOk => {
unwrapOr: _ => val,
unwrapFail: throwReferenceError('Cannot unwrap a success'),
map: (fn: (val: T) => M) => ok(fn(val)),
- mapFail: (_: (err: E) => M) => ok(val)
+ mapFail: (_: (err: E) => M) => ok(val),
+ flatMap: (fn: (val: T) => IResult) => fn(val)
}
}
@@ -62,7 +65,8 @@ export const fail = (err: E): IResultFail => {
unwrapOr: opt => opt,
unwrapFail: returnValue(err),
map: (_: (val: T) => M) => fail(err),
- mapFail: (fn: (err: E) => M) => fail(fn(err))
+ mapFail: (fn: (err: E) => M) => fail(fn(err)),
+ flatMap: (_: (val: T) => IResult) => fail(err)
}
}
diff --git a/test/monads/result.spec.ts b/test/monads/result.spec.ts
index 3159b4f..cd899f7 100644
--- a/test/monads/result.spec.ts
+++ b/test/monads/result.spec.ts
@@ -55,6 +55,14 @@ describe('result', () => {
.unwrap()
expect(sut).toEqual(1)
})
+
+ it('should flatMap', () => {
+ const sut = ok(1)
+ .flatMap(a => ok(a.toString()))
+ .unwrap()
+
+ expect(sut).toEqual('1')
+ })
})
describe('fail', () => {
@@ -107,6 +115,14 @@ describe('result', () => {
.unwrapFail()
expect(sut).toEqual('1')
})
+
+ it('should not flatMap', () => {
+ const sut = fail(1)
+ .flatMap(a => ok(a.toString()))
+ .unwrapFail()
+
+ expect(sut).toEqual(1)
+ })
})
describe('result', () => {
From aaab5b46f967552e844745648d1fc5b975763afb Mon Sep 17 00:00:00 2001
From: Patrick Michalina
Date: Wed, 9 Jan 2019 22:56:23 -0600
Subject: [PATCH 7/8] match
---
src/interfaces/result.interface.ts | 0
src/monads/result.ts | 19 ++++++++++++++-----
test/monads/result.spec.ts | 20 ++++++++++++++++++++
3 files changed, 34 insertions(+), 5 deletions(-)
create mode 100644 src/interfaces/result.interface.ts
diff --git a/src/interfaces/result.interface.ts b/src/interfaces/result.interface.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/monads/result.ts b/src/monads/result.ts
index e8ec296..a88d636 100644
--- a/src/monads/result.ts
+++ b/src/monads/result.ts
@@ -9,6 +9,11 @@ const throwReferenceError = (message: string) => () => { throw new ReferenceErro
type Predicate = () => boolean
+export interface IResultMatchPattern {
+ readonly ok: (val: T) => U
+ readonly fail: (val: E) => U
+}
+
export interface IResult {
isOk(): boolean
isFail(): boolean
@@ -17,16 +22,17 @@ export interface IResult {
unwrap(): T | never
unwrapOr(opt: T): T
unwrapFail(): E | never
+ match(fn: IResultMatchPattern): M
map(fn: (val: T) => M): IResult
mapFail(fn: (err: E) => M): IResult
flatMap(fn: (val: T) => IResult): IResult
- // match(fn: Match): U
}
export interface IResultOk extends IResult {
unwrap(): T
unwrapOr(opt: T): T
- unwrapFail(): never,
+ unwrapFail(): never
+ match(fn: IResultMatchPattern): M
map(fn: (val: T) => M): IResultOk
mapFail(fn: (err: E) => M): IResultOk
}
@@ -34,7 +40,8 @@ export interface IResultOk extends IResult {
export interface IResultFail extends IResult {
unwrap(): never
unwrapOr(opt: T): T
- unwrapFail(): E,
+ unwrapFail(): E
+ match(fn: IResultMatchPattern): M
map(fn: (val: T) => M): IResultFail
mapFail(fn: (err: E) => M): IResultFail
flatMap(fn: (val: T) => IResult): IResultFail
@@ -51,7 +58,8 @@ export const ok = (val: T): IResultOk => {
unwrapFail: throwReferenceError('Cannot unwrap a success'),
map: (fn: (val: T) => M) => ok(fn(val)),
mapFail: (_: (err: E) => M) => ok(val),
- flatMap: (fn: (val: T) => IResult) => fn(val)
+ flatMap: (fn: (val: T) => IResult) => fn(val),
+ match: (fn: IResultMatchPattern) => fn.ok(val)
}
}
@@ -66,7 +74,8 @@ export const fail = (err: E): IResultFail => {
unwrapFail: returnValue(err),
map: (_: (val: T) => M) => fail(err),
mapFail: (fn: (err: E) => M) => fail(fn(err)),
- flatMap: (_: (val: T) => IResult) => fail(err)
+ flatMap: (_: (val: T) => IResult) => fail(err),
+ match: (fn: IResultMatchPattern) => fn.fail(err)
}
}
diff --git a/test/monads/result.spec.ts b/test/monads/result.spec.ts
index cd899f7..63ca862 100644
--- a/test/monads/result.spec.ts
+++ b/test/monads/result.spec.ts
@@ -63,6 +63,16 @@ describe('result', () => {
expect(sut).toEqual('1')
})
+
+ it('should match', () => {
+ const sut = ok(1)
+ .match({
+ fail: _ => 2,
+ ok: val => val
+ })
+
+ expect(sut).toEqual(1)
+ })
})
describe('fail', () => {
@@ -123,6 +133,16 @@ describe('result', () => {
expect(sut).toEqual(1)
})
+
+ it('should match', () => {
+ const sut = fail(1)
+ .match({
+ fail: _ => 2,
+ ok: val => val
+ })
+
+ expect(sut).toEqual(2)
+ })
})
describe('result', () => {
From c0d036df3859e1977b36115f5e3a38f6d5c6b4ae Mon Sep 17 00:00:00 2001
From: Patrick Michalina
Date: Wed, 9 Jan 2019 22:59:23 -0600
Subject: [PATCH 8/8] curried
---
src/monads/result.ts | 9 +++++++++
test/monads/result.spec.ts | 7 ++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/monads/result.ts b/src/monads/result.ts
index a88d636..ea70ee3 100644
--- a/src/monads/result.ts
+++ b/src/monads/result.ts
@@ -86,3 +86,12 @@ export const result = (predicate: Predicate, okValue: T, failValue: E): IR
predicate()
? ok(okValue)
: fail(failValue)
+
+/**
+* Utility function to quickly create ok/fail pairs, curried variant.
+*/
+export const curriedResult =
+ (predicate: Predicate) =>
+ (okValue: T) =>
+ (failValue: E): IResult =>
+ result(predicate, okValue, failValue)
diff --git a/test/monads/result.spec.ts b/test/monads/result.spec.ts
index 63ca862..199e663 100644
--- a/test/monads/result.spec.ts
+++ b/test/monads/result.spec.ts
@@ -1,4 +1,4 @@
-import { ok, fail, result } from '../../src/monads'
+import { ok, fail, result, curriedResult } from '../../src/monads'
describe('result', () => {
describe('ok', () => {
@@ -155,5 +155,10 @@ describe('result', () => {
const sut = result(() => 1 + 1 === 2, true, 'FAILURE!')
expect(sut.isOk()).toEqual(true)
})
+
+ it('should return curried', () => {
+ const sut = curriedResult(() => 1 + 1 === 2)(true)('FAILURE!')
+ expect(sut.isOk()).toEqual(true)
+ })
})
})