-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
…try-monad Feature/#14 implement try monad
- Loading branch information
Showing
10 changed files
with
209 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires,no-undef | ||
const fs = require('fs'); | ||
fs.rmdirSync('dist', {recursive: true}); | ||
fs.rmdirSync('dist', { recursive: true }); | ||
fs.rmdirSync('coverage', { recursive: true }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,5 @@ | ||
sonar.projectKey=SimonHarmonicMinor_try-monad | ||
sonar.organization=simonharmonicminor | ||
sonar.projectName=try-monad | ||
sonar.typescript.lcov.reportPaths=coverage/lcov.info | ||
sonar.sources=src | ||
sonar.sourceEncoding=UTF-8 | ||
sonar.coverage.exclusions=**/*.test.*,**/index.ts,**/index.tsx | ||
sonar.javascript.lcov.reportPaths=coverage/lcov.info | ||
sonar.sourceEncoding=UTF-8 |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import Try from '../index'; | ||
|
||
describe('"Try" monad test suites', () => { | ||
it('"success" should return successfully evaluated monad', () => { | ||
expect(Try.success(1).orElseThrow()).toEqual(1); | ||
}); | ||
|
||
it('"error" should return failure monad', () => { | ||
expect(() => Try.error().orElseThrow()).toThrowError(); | ||
}); | ||
|
||
it('Should be evaluated lazily until terminal operation call', () => { | ||
const mockFunction = jest.fn(); | ||
const tryMonad = Try.of(() => 1) | ||
.map((v) => { | ||
mockFunction(); | ||
return v + 1; | ||
}) | ||
.map((v) => { | ||
mockFunction(); | ||
return v.toString(); | ||
}); | ||
expect(mockFunction).toHaveBeenCalledTimes(0); | ||
expect(tryMonad.orElse('')).not.toBeNull(); | ||
expect(mockFunction).toHaveBeenCalledTimes(2); | ||
}); | ||
|
||
it('Should map the input and return the result value', () => { | ||
const tryMonad = Try.of(() => 1) | ||
.map((v) => v + 200) | ||
.map((v) => v.toString()) | ||
.map((v) => v + 'ty'); | ||
expect(tryMonad.orElse('wrong value')).toEqual('201ty'); | ||
}); | ||
|
||
it('Should map the input and return the default value', () => { | ||
const tryMonad = Try.of(() => 1) | ||
.map((v) => v + 200) | ||
.map((v) => v.toString()) | ||
.map((v) => v + 'ty') | ||
.map<string>(() => { | ||
throw new Error('error'); | ||
}) | ||
.map((v) => v.length); | ||
expect(tryMonad.orElse(-10)).toEqual(-10); | ||
}); | ||
|
||
it('Should flat mat the input and return the result value', () => { | ||
const tryMonad = Try.of(() => 1) | ||
.flatMap((v) => Try.success(v + 1)) | ||
.flatMap((v) => Try.success(v.toString() + 'ui')); | ||
expect(tryMonad.orElse('wrong value')).toEqual('2ui'); | ||
}); | ||
|
||
it('Should flat mat the input and return the default value', () => { | ||
const tryMonad = Try.of(() => 1) | ||
.flatMap((v) => Try.success(v + 1)) | ||
.flatMap<string>(() => Try.error()) | ||
.flatMap((v) => Try.success(v.toString() + 'ui')); | ||
expect(tryMonad.orElse('wrong value')).toEqual('wrong value'); | ||
}); | ||
|
||
it('Should filter the value and return the result', () => { | ||
const tryMonad = Try.of(() => 'str') | ||
.filter((value) => value.length > 0) | ||
.filter((value) => value.includes('st')); | ||
expect(tryMonad.orElse('wrong value')).toEqual('str'); | ||
}); | ||
|
||
it('Should filter the value and return the default one', () => { | ||
const tryMonad = Try.of(() => 'str') | ||
.filter((value) => value.length === 5) | ||
.filter((value) => value.includes('st')); | ||
expect(tryMonad.orElse('wrong value')).toEqual('wrong value'); | ||
}); | ||
|
||
it('Should return the first "Try" if it succeeds', () => { | ||
const tryMonad = Try.of(() => 1) | ||
.map((value) => value + 600) | ||
.orElseTry(() => -1); | ||
expect(tryMonad.orElse(-2)).toEqual(601); | ||
}); | ||
|
||
it('Should return the last "Try" if the first one fails', () => { | ||
const tryMonad = Try.of(() => 1) | ||
.map<number>(() => { | ||
throw new Error('error'); | ||
}) | ||
.orElseTry(() => -1); | ||
expect(tryMonad.orElse(-2)).toEqual(-1); | ||
}); | ||
|
||
it('Should provide the error that led to the bug', () => { | ||
const bug = new Error('the error that led to the bug'); | ||
const tryMonad = Try.of(() => 22) | ||
.map((value) => value + 67) | ||
.map<string>(() => { | ||
throw bug; | ||
}); | ||
expect( | ||
tryMonad.orElseGet((error) => { | ||
expect(error).toStrictEqual(bug); | ||
return 'default string'; | ||
}) | ||
).toEqual('default string'); | ||
}); | ||
|
||
it('Should throw the provided error if "Try" fails', () => { | ||
const tryMonad = Try.of(() => 22) | ||
.map((value) => value + 67) | ||
.map<string>(() => { | ||
throw new Error('error'); | ||
}); | ||
const errorToThrow = new Error('error to throw'); | ||
expect(() => tryMonad.orElseThrow(() => errorToThrow)).toThrowError( | ||
errorToThrow | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
export default class Try {} | ||
import { Try } from './try'; | ||
|
||
export default Try; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
export class Try<T> { | ||
private readonly supplier: () => T; | ||
|
||
private constructor(supplier: () => T) { | ||
this.supplier = supplier; | ||
} | ||
|
||
public static of<T>(supplier: () => T): Try<T> { | ||
return new Try<T>(supplier); | ||
} | ||
|
||
public static success<T>(value: T): Try<T> { | ||
return new Try<T>(() => value); | ||
} | ||
|
||
public static error<T>(exception?: Error): Try<T> { | ||
return new Try<T>(() => { | ||
if (exception) { | ||
throw exception; | ||
} | ||
throw new Error('Try is empty'); | ||
}); | ||
} | ||
|
||
public map<U>(mapper: (value: T) => U): Try<U> { | ||
return new Try<U>(() => mapper(this.supplier())); | ||
} | ||
|
||
public flatMap<U>(mapper: (value: T) => Try<U>): Try<U> { | ||
return new Try<U>(() => mapper(this.supplier()).orElseThrow()); | ||
} | ||
|
||
public filter(predicate: (value: T) => boolean): Try<T> { | ||
return new Try<T>(() => { | ||
const value = this.supplier(); | ||
if (predicate(value)) { | ||
return value; | ||
} | ||
throw new Error('Predicate returned false'); | ||
}); | ||
} | ||
|
||
public orElseTry(elseCondition: () => T): Try<T> { | ||
return new Try<T>(() => { | ||
try { | ||
return this.supplier(); | ||
} catch { | ||
return elseCondition(); | ||
} | ||
}); | ||
} | ||
|
||
public orElse(value: T): T { | ||
try { | ||
return this.supplier(); | ||
} catch { | ||
return value; | ||
} | ||
} | ||
|
||
public orElseGet(elseValueSupplier: (error: unknown) => T): T { | ||
try { | ||
return this.supplier(); | ||
} catch (error) { | ||
return elseValueSupplier(error); | ||
} | ||
} | ||
|
||
public orElseThrow(errorSupplier?: () => Error): T { | ||
try { | ||
return this.supplier(); | ||
} catch (error) { | ||
if (errorSupplier) { | ||
throw errorSupplier(); | ||
} | ||
throw error; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters