diff --git a/src/interfaces/maybe.interface.ts b/src/interfaces/maybe.interface.ts index d6ee377..d400d2a 100644 --- a/src/interfaces/maybe.interface.ts +++ b/src/interfaces/maybe.interface.ts @@ -98,4 +98,9 @@ export interface IMaybe extends IMonad { * otherwise return an empty Maybe */ filter(fn: (t: T) => boolean): IMaybe -} \ No newline at end of file + + /** + * Apply a function wrapped in Maybe + */ + apply(fab: IMaybe<(t: T) => R>): IMaybe +} diff --git a/src/monads/maybe.ts b/src/monads/maybe.ts index 9042dbf..94889cb 100644 --- a/src/monads/maybe.ts +++ b/src/monads/maybe.ts @@ -14,6 +14,7 @@ const tapSome = (value?: T) => (fn: (val: NonNullable) => void) => isNotEm const match = (value?: T) => (pattern: IMaybePattern) => isEmpty(value) ? pattern.none() : pattern.some(value as NonNullable) const map = (value?: T) => (fn: (t: NonNullable) => R) => isEmpty(value) ? maybe() : maybe(fn(value as NonNullable)) const flatMap = (value?: T) => (fn: (d: NonNullable) => IMaybe) => isEmpty(value) ? maybe() : fn(value as NonNullable) +const apply = (value?: T) => (maybeFn: IMaybe<(t: T) => R>) => maybeFn.flatMap(f => map(value)(f)) const flatMapAuto = (value?: T) => (fn: (d: NonNullable) => R) => isEmpty(value) ? maybe() : maybe(fn(value as NonNullable)) const valueOrThrow = (value?: T) => (msg?: string) => isEmpty(value) ? (() => { throw Error(msg) })() : value as NonNullable @@ -42,7 +43,8 @@ export const maybe = (value?: T): IMaybe> => { map: map(value), flatMap: flatMap(value), flatMapAuto: flatMapAuto(value), - filter: filter(value) + filter: filter(value), + apply: apply(value) } } diff --git a/test/monads/maybe.spec.ts b/test/monads/maybe.spec.ts index b51d548..1e94bf6 100644 --- a/test/monads/maybe.spec.ts +++ b/test/monads/maybe.spec.ts @@ -495,4 +495,23 @@ describe('Maybe', () => { .catch(error => expect(error).toEqual('err')) }) }) + + describe('apply', () => { + it('should return none in nullish cases', () => { + const thisNone = maybe() + const fnNone = maybe<(n: number) => number>() + const thisSome = maybe(5) + const fnSome = maybe((a: number) => a * 2) + + expect(thisNone.apply(fnNone).isNone()).toBe(true) + expect(thisNone.apply(fnSome).isNone()).toBe(true) + expect(thisSome.apply(fnNone).isNone()).toBe(true) + }) + + it('should apply the function in a maybe in someish cases', () => { + const a = maybe(5) + const f = maybe((a: number) => a * 2) + expect(a.apply(f).valueOrThrow()).toBe(10) + }) + }) })