Last active
October 15, 2021 06:28
-
-
Save lupuszr/f73baa7ef559f77e6e847ba75ed97182 to your computer and use it in GitHub Desktop.
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
// Proper try data structure implementation in typescript | |
// pass as thunk | |
type $<A> = () => A | |
function $<A>(a: A): $<A>{ | |
return () => a | |
} | |
// make function lazy | |
function $fn<A extends (...args: any) => any>(a: A): ((...p: Parameters<A>) => $<ReturnType<A>>) { | |
return (...p: [Parameters<A>]) => () => { | |
return a(...p) | |
} | |
} | |
abstract class Try<A> { | |
abstract map<B>(this: Try<A>, fn: (a: A) => B): Try<B> | |
abstract chain<B>(this: Try<A>, fn: (a: A) => Try<B>): Try<B>; | |
abstract orElseTry(this: Try<A>, fallback: Try<A>): Try<A>; | |
abstract filterTry(this: Try<A>, fn: (a: A) => Boolean): Try<A>; | |
abstract ap<B>(this: Try<A>, vT: Try<Fn<A, B>>): Try<B>; | |
apply<B, C, X>(this: Try<A extends Fn<C, B> ? A : never>, vT: Try<C>): Try<B> { | |
return vT.chain(v => { | |
return this.map(fn => fn(v)) | |
}) | |
} | |
static liftFn<A, B>(fn: Fn<A, B>): Try<Fn<A, B>> { | |
return mkTry($(fn)) | |
} | |
static lift<A, B>(val: A): Try<A> { | |
return mkTry($(val)) | |
} | |
} | |
function mkTry<A>(value: $<A>): Try<A> { | |
try { | |
const a = value(); | |
return Success.of(a) | |
} catch (error) { | |
return Failure.of(error as Error) | |
} | |
} | |
class Success<A> extends Try<A> { | |
value: A | |
__tag: "Success" = "Success"; | |
constructor(a: A) { | |
super() | |
this.value = a; | |
} | |
static of<A>(value: A): Try<A> { return new Success(value); } | |
map<B>(fn: (a: A) => B): Try<B> { | |
const val = this.value; | |
const x = $fn(fn)(val); | |
return mkTry<B>(x) | |
} | |
chain<B>(fn: (a: A) => Try<B>): Try<B> { | |
return fn(this.value); | |
} | |
orElseTry(fallback: Try<A>): Try<A> { | |
return this; | |
} | |
filterTry(fn: (a: A) => Boolean): Try<A> { | |
if (fn(this.value)) { | |
return this; | |
} else { | |
return mkTry(() => {throw new Error("NoSuchElementException")}) | |
} | |
} | |
ap<B>(fnT: Try<Fn<A, B>>): Try<B> { | |
return this.chain(v => { | |
return fnT.map(fn => fn(v)) | |
}) | |
} | |
} | |
class Failure<A> extends Try<A> { | |
value: Error; | |
__tag: "Failure" = "Failure"; | |
constructor(e: Error) { | |
super(); | |
this.value = e; | |
} | |
static of<A>(value: Error): Try<A> { return new Failure(value); } | |
map<B>(fn: (a: A) => B): Try<B> { | |
return Failure.of(this.value); | |
} | |
chain<B>(fn: (a: A) => Try<B>): Try<B> { | |
return Failure.of(this.value) | |
} | |
orElseTry(fallback: Try<A>): Try<A> { | |
return fallback; | |
} | |
filterTry(fn: (a: A) => Boolean): Try<A> { | |
return this; | |
} | |
ap<B>(this: Try<A>,vT: Try<Fn<A,B>>): Try<B> { | |
throw this; | |
} | |
} | |
// maybe do it once | |
// const convertPromise = <A>(promise: Promise<A>, newPromiseProvider = Promise) => newPromiseProvider.resolve(promise) | |
// // Examples:: | |
// // define a simple function that throws an error | |
const x = (a: number) => { | |
if (a % 2 === 0) { | |
return a | |
} | |
throw new Error(`value: ${a} is even`); | |
} | |
const div4 = (a: number) => { | |
if (a % 4 === 0) { | |
return a | |
} | |
throw new Error(`value: ${a} is not dividible by 4`); | |
} | |
// make it lazy | |
const lazyX = $fn(x) | |
const lazyDiv = $fn(div4) | |
// // wrap inside try, map chain and filter | |
const a1 = Try.lift(12) | |
.map(a => a + 10) | |
.map(b => (b * 2) - 4) | |
.chain(a => mkTry(lazyDiv(a))); | |
console.log(a1); | |
// const a2 = Try.liftFn(div4).apply(Try.lift(2)) //.apF(mkTry(() => 2)) | |
// console.log(a2); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment