Skip to content

Instantly share code, notes, and snippets.

@OliverBrotchie
Created September 25, 2021 23:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OliverBrotchie/319ae829eb901f33072ee9e3cc933153 to your computer and use it in GitHub Desktop.
Save OliverBrotchie/319ae829eb901f33072ee9e3cc933153 to your computer and use it in GitHub Desktop.
Types for Optionals
const None = Symbol("None");
const DefaultErr = Symbol("Err");
type Option<T> = ((s: (val: T) => T) => T) | typeof None;
type Some<T> = (val: T) => Option<T>;
const Some: <T>(val: T) => Option<T> = (val) => (s) => s(val);
type Result<T, E> = (o: (val: T) => E, s: (val: symbol) => E) => E;
type Err<T> = T;
const Err: <A, B>(val: symbol) => Result<A, B> = (val: symbol) => (_, g) =>
g(val);
const Ok: <T, E>(val: T) => Result<T, E> = (val) => (f, _) => f(val);
function unwrap<T, U>(value: Option<T> | Result<T, U>): T {
if ((value as Option<T>) === None) throw `unwrap called on None`;
else if (typeof value == "function") {
return (value as Result<T, T>)(
(val) => val as T,
(val) => {
throw `unwrap called on ${val.toString()}`;
}
);
} else return value as unknown as T;
}
// Usage of both result and option in a function
function divide(
numerator: unknown,
denominator: unknown
): Result<Option<number>, Err<typeof DefaultErr>> {
if (typeof numerator != "number" || typeof denominator != "number") {
return Err(DefaultErr);
} else if (denominator == 0) {
return Ok(None);
} else {
return Ok(Some(numerator / denominator));
}
}
console.log(unwrap(unwrap(divide(4, 2))));
export class Option<T> {
private constructor(private isSome: boolean, private value?: T) {}
unwrap(): T {
if (!this.isSome) {
throw new Error("Option is none");
}
return this.value as T;
}
static Some<T>(value: T) {
return new Option(true, value);
}
static None: Option<never> = new Option(false);
}
function divide(numerator: number, denominator: number): Option<number> {
if (denominator == 0) {
return Option.None;
} else {
return Option.Some(numerator / denominator);
}
}
console.log(divide(4, 2).unwrap());
function createSome<T>(value: T) {
return new SomeClass(value);
}
export interface OptionalGeneric<T> {
isSome: boolean;
unwrap(): T;
map<U>(transform: (value: T) => U): Option<U>;
or<U>(value: U): T | U;
}
export class SomeClass<T> implements OptionalGeneric<T> {
readonly value: T;
readonly isSome = true;
constructor(value: T) {
this.value = value;
}
unwrap(): T {
return this.value;
}
map<U>(transform: (value: T) => U) {
return new SomeClass(transform(this.value));
}
or<U>(_: U) {
return this.value;
}
}
class NoneClass implements OptionalGeneric<never> {
readonly isSome = false;
unwrap(): never {
throw new Error("called unwrap on None");
}
map() {
return this;
}
or<U>(value: U) {
return value;
}
}
export type Some<T> = SomeClass<T>;
export const Some = createSome;
const instance = new NoneClass();
export type None = typeof instance;
export const None = instance;
export type Option<T> = Some<T> | None;
function divide(numerator: number, denominator: number): Option<number> {
if (denominator == 0) {
return None;
} else {
return Some(numerator / denominator);
}
}
console.log(divide(4, 2).unwrap());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment