From d37f18573c6627cc4ced6f2a7b4a990fbd0cba9d Mon Sep 17 00:00:00 2001 From: Mauko Quiroga Date: Sat, 17 Aug 2024 16:50:02 +0200 Subject: [PATCH] feat: add maybe map --- src/types.ts | 1 + src/utils/adt/maybe.ts | 20 ++++++++++++++++++++ src/utils/typeclass/functor.ts | 2 ++ test/unit/utils/adt/maybe.test.ts | 22 +++++++++++++++++++++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 256e9fc..fe78895 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,3 +14,4 @@ export type { PostalAddress } from '~/stores/PostalAddressStore' export type { GeoCoordinates } from '~/stores/GeoCoordinatesStore' export type { IsString } from '~/utils/types/string' export type { IsNumber } from '~/utils/types/number' +export type { Map } from '~/utils/typeclass/functor' diff --git a/src/utils/adt/maybe.ts b/src/utils/adt/maybe.ts index 2ad12c6..d3dff69 100644 --- a/src/utils/adt/maybe.ts +++ b/src/utils/adt/maybe.ts @@ -88,3 +88,23 @@ export const match: Match = (onNothing, onJust) => (x) => .with({ _tag: 'Nothing' }, onNothing) .with({ _tag: 'Just' }, ({ value }) => onJust(value)) .exhaustive() + +/** + * Maybe functor to wrap/unwrap a function. + * + * @param {Function} f The mapping function that transforms the input value. + * @returns {Function} A new function that takes a `Maybe` container and applies + * the mapping function to its value. + */ +export type Map = (f: (x: A) => B) => (Fx: Maybe) => Maybe + +/** + * Maybe functor to wrap/unwrap a function. + * + * @todo Add support for curried functions. + */ +export const map: Map = (f) => + match( + () => nothing, + (a) => just(f(a)) + ) diff --git a/src/utils/typeclass/functor.ts b/src/utils/typeclass/functor.ts index a176b0a..d0a2bb8 100644 --- a/src/utils/typeclass/functor.ts +++ b/src/utils/typeclass/functor.ts @@ -3,3 +3,5 @@ * * @module functor */ + +export type { Map } from '~/utils/adt/maybe' diff --git a/test/unit/utils/adt/maybe.test.ts b/test/unit/utils/adt/maybe.test.ts index 5bea704..96fa7a9 100644 --- a/test/unit/utils/adt/maybe.test.ts +++ b/test/unit/utils/adt/maybe.test.ts @@ -3,7 +3,7 @@ import type { Matcher } from '~/utils/adt/maybe' import { describe, expect, expectTypeOf, test } from 'vitest' -import { isJust, isNothing, just, match, nothing } from '~/utils/adt/maybe' +import { isJust, isNothing, just, map, match, nothing } from '~/utils/adt/maybe' describe('Maybe', () => { describe('Given a value or the absence thereof', () => { @@ -98,3 +98,23 @@ describe('match/2', () => { }) }) }) + +describe('map/1', () => { + describe('Given a function', () => { + type Inc = (a: A) => A + const inc: Inc = (a) => a + 1 + + type MaybeAdd = (a: Maybe) => Maybe + const maybeAdd: MaybeAdd = map(inc) + + test('When lifting it with "just"', () => { + const result = maybeAdd(just(1)) + expect(result).toStrictEqual(just(2)) + }) + + test('When lifting it with "nothing"', () => { + const result = maybeAdd(nothing) + expect(result).toBe(nothing) + }) + }) +})