From a64a48b3d10dcf25131ca93e4deb2c41108ab9ea Mon Sep 17 00:00:00 2001 From: Ignatius Bagus Date: Thu, 6 Apr 2023 17:13:23 +0700 Subject: [PATCH 1/6] rearrange --- src/core/index.ts | 3 +-- src/core/lambda/index.spec.ts | 8 +++----- src/core/lambda/index.ts | 2 ++ 3 files changed, 6 insertions(+), 7 deletions(-) create mode 100644 src/core/lambda/index.ts diff --git a/src/core/index.ts b/src/core/index.ts index fa5dd649..3972f8be 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,7 +1,6 @@ +export * from './lambda/index.js'; export * from './processor/index.js'; export * from './standard/index.js'; export * as compare from './compare/index.js'; -export { default as curry } from './lambda/curry.js'; -export { default as pipe } from './lambda/pipe.js'; export { default as unique } from './standard/unique.js'; diff --git a/src/core/lambda/index.spec.ts b/src/core/lambda/index.spec.ts index e51f52fc..e706a9aa 100644 --- a/src/core/lambda/index.spec.ts +++ b/src/core/lambda/index.spec.ts @@ -1,8 +1,6 @@ import { suite } from 'uvu'; import * as assert from 'uvu/assert'; - -import curry from './curry.js'; -import pipe from './pipe.js'; +import * as lambda from './index.js'; const basics = { curry: suite('lambda:curry'), @@ -11,7 +9,7 @@ const basics = { basics.curry('properly curry a function', () => { const sum = (a: number, b: number, c: number) => a + b + c; - const curried = curry(sum); + const curried = lambda.curry(sum); assert.type(curried, 'function'); assert.type(curried(1), 'function'); @@ -25,7 +23,7 @@ basics.pipe('properly apply functions in ltr order', () => { const name = (v: T) => v.name; const split = (v: string) => v.split(''); - const pipeline = pipe(name, cap, split); + const pipeline = lambda.pipe(name, cap, split); assert.equal(pipeline({ name: 'mom' }), ['M', 'O', 'M']); }); diff --git a/src/core/lambda/index.ts b/src/core/lambda/index.ts new file mode 100644 index 00000000..a2e98880 --- /dev/null +++ b/src/core/lambda/index.ts @@ -0,0 +1,2 @@ +export { default as curry } from './curry.js'; +export { default as pipe } from './pipe.js'; From 153a2dead8ef835795887f900e87d328116d9f47 Mon Sep 17 00:00:00 2001 From: Ignatius Bagus Date: Thu, 6 Apr 2023 17:28:17 +0700 Subject: [PATCH 2/6] failing test --- src/core/lambda/index.spec.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/core/lambda/index.spec.ts b/src/core/lambda/index.spec.ts index e706a9aa..50c7ef77 100644 --- a/src/core/lambda/index.spec.ts +++ b/src/core/lambda/index.spec.ts @@ -7,6 +7,10 @@ const basics = { pipe: suite('lambda:pipe'), }; +const composite = { + masked: suite('lambda:mask+reveal'), +}; + basics.curry('properly curry a function', () => { const sum = (a: number, b: number, c: number) => a + b + c; const curried = lambda.curry(sum); @@ -28,3 +32,21 @@ basics.pipe('properly apply functions in ltr order', () => { }); Object.values(basics).forEach((v) => v.run()); + +// ---- composite ---- + +composite.masked('properly mask and reveal a value', () => { + const { mask, reveal } = lambda; + + const answer = mask.of(() => 42); + assert.equal(reveal(answer).expect('unreachable'), 42); + + let maybe: string | null | undefined; + let wrapped = mask.wrap(maybe); + assert.equal(reveal(wrapped).or('2023-04-04'), '2023-04-04'); + + wrapped = mask.wrap('2023-04-06'); + assert.equal(reveal(wrapped).expect('unreachable'), '2023-04-06'); +}); + +Object.values(composite).forEach((v) => v.run()); From f737f393371d0c2f545535860a7b7f3b081a03f8 Mon Sep 17 00:00:00 2001 From: Ignatius Bagus Date: Thu, 6 Apr 2023 17:31:01 +0700 Subject: [PATCH 3/6] draft implementation --- src/core/lambda/index.ts | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/core/lambda/index.ts b/src/core/lambda/index.ts index a2e98880..0c8144ca 100644 --- a/src/core/lambda/index.ts +++ b/src/core/lambda/index.ts @@ -1,2 +1,47 @@ export { default as curry } from './curry.js'; export { default as pipe } from './pipe.js'; + +type Masked = { $kind: 'none' } | { $kind: 'some'; value: T }; + +const none = { $kind: 'none' } as const; +function some(v: T) { + return { $kind: 'some', value: v } as const; +} + +function cast(fn: (x: X) => x is Y) { + return (arg: X): Masked => { + try { + return fn(arg) ? some(arg) : none; + } catch { + return none; + } + }; +} + +export const mask = { + of(fn: () => T): Masked { + try { + return some(fn()); + } catch { + return none; + } + }, + + async resolve(p: Promise): Promise> { + return p.then((v) => some(v)).catch(() => none); + }, + + wrap: cast((i: T | undefined | null): i is T => i != null), +} as const; + +export function reveal(opt: Masked) { + return { + expect(message: string) { + if (opt.$kind === 'some') return opt.value; + throw new Error(message); + }, + or(fallback: T): T { + return opt.$kind === 'some' ? opt.value : fallback; + }, + }; +} From 6ecf3308d4e1b0416654b2a2131204685485d970 Mon Sep 17 00:00:00 2001 From: Ignatius Bagus Date: Mon, 10 Apr 2023 18:36:09 +0700 Subject: [PATCH 4/6] revamp kind values --- src/core/lambda/index.ts | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/core/lambda/index.ts b/src/core/lambda/index.ts index 0c8144ca..9de5e2c4 100644 --- a/src/core/lambda/index.ts +++ b/src/core/lambda/index.ts @@ -1,19 +1,22 @@ export { default as curry } from './curry.js'; export { default as pipe } from './pipe.js'; -type Masked = { $kind: 'none' } | { $kind: 'some'; value: T }; - -const none = { $kind: 'none' } as const; -function some(v: T) { - return { $kind: 'some', value: v } as const; +function error(msg?: string) { + const error = msg || ''; + return { $kind: 'error' as const, error }; +} +function success(value: T) { + return { $kind: 'success' as const, value }; } +type Masked = ReturnType | ReturnType>; + function cast(fn: (x: X) => x is Y) { return (arg: X): Masked => { try { - return fn(arg) ? some(arg) : none; + return fn(arg) ? success(arg) : error(); } catch { - return none; + return error(); } }; } @@ -21,14 +24,14 @@ function cast(fn: (x: X) => x is Y) { export const mask = { of(fn: () => T): Masked { try { - return some(fn()); + return success(fn()); } catch { - return none; + return error(); } }, async resolve(p: Promise): Promise> { - return p.then((v) => some(v)).catch(() => none); + return p.then((v) => success(v)).catch(() => error()); }, wrap: cast((i: T | undefined | null): i is T => i != null), @@ -37,11 +40,11 @@ export const mask = { export function reveal(opt: Masked) { return { expect(message: string) { - if (opt.$kind === 'some') return opt.value; + if (opt.$kind === 'success') return opt.value; throw new Error(message); }, or(fallback: T): T { - return opt.$kind === 'some' ? opt.value : fallback; + return opt.$kind === 'success' ? opt.value : fallback; }, }; } From bb9addcee5a61ab2d7239ced5e6b1a6ca513f522 Mon Sep 17 00:00:00 2001 From: Ignatius Bagus Date: Mon, 10 Apr 2023 18:37:04 +0700 Subject: [PATCH 5/6] rename type to Result --- src/core/lambda/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/lambda/index.ts b/src/core/lambda/index.ts index 9de5e2c4..b8aa8801 100644 --- a/src/core/lambda/index.ts +++ b/src/core/lambda/index.ts @@ -9,10 +9,10 @@ function success(value: T) { return { $kind: 'success' as const, value }; } -type Masked = ReturnType | ReturnType>; +type Result = ReturnType | ReturnType>; function cast(fn: (x: X) => x is Y) { - return (arg: X): Masked => { + return (arg: X): Result => { try { return fn(arg) ? success(arg) : error(); } catch { @@ -22,7 +22,7 @@ function cast(fn: (x: X) => x is Y) { } export const mask = { - of(fn: () => T): Masked { + of(fn: () => T): Result { try { return success(fn()); } catch { @@ -30,14 +30,14 @@ export const mask = { } }, - async resolve(p: Promise): Promise> { + async resolve(p: Promise): Promise> { return p.then((v) => success(v)).catch(() => error()); }, wrap: cast((i: T | undefined | null): i is T => i != null), } as const; -export function reveal(opt: Masked) { +export function reveal(opt: Result) { return { expect(message: string) { if (opt.$kind === 'success') return opt.value; From 9b952f0529e05ab434b03f403ad68c310380c90b Mon Sep 17 00:00:00 2001 From: Ignatius Bagus Date: Fri, 21 Apr 2023 11:25:08 +0700 Subject: [PATCH 6/6] remove prefix --- src/core/lambda/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/lambda/index.ts b/src/core/lambda/index.ts index b8aa8801..834851a5 100644 --- a/src/core/lambda/index.ts +++ b/src/core/lambda/index.ts @@ -3,10 +3,10 @@ export { default as pipe } from './pipe.js'; function error(msg?: string) { const error = msg || ''; - return { $kind: 'error' as const, error }; + return { kind: 'error' as const, error }; } function success(value: T) { - return { $kind: 'success' as const, value }; + return { kind: 'success' as const, value }; } type Result = ReturnType | ReturnType>; @@ -40,11 +40,11 @@ export const mask = { export function reveal(opt: Result) { return { expect(message: string) { - if (opt.$kind === 'success') return opt.value; + if (opt.kind === 'success') return opt.value; throw new Error(message); }, or(fallback: T): T { - return opt.$kind === 'success' ? opt.value : fallback; + return opt.kind === 'success' ? opt.value : fallback; }, }; }