Last active
March 20, 2023 20:58
-
-
Save tim-smart/8ff4ac7c497f422e62bb88e731428087 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
node_modules | |
pnpm-lock.yaml | |
*.json |
This file contains hidden or 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
import * as Data from "@effect/data/Data" | |
type Simplify<A> = { [K in keyof A]: A[K] } & {} | |
// ==================== | |
// Tagged enums (no generics) | |
// ==================== | |
export type TaggedEnumConstructor< | |
A extends Record<string, Record<string, any>>, | |
> = <K extends keyof A>( | |
tag: K, | |
) => Data.Case.Constructor< | |
Data.Data< | |
Simplify< | |
Readonly<A[K]> & { | |
readonly _tag: K | |
} | |
> | |
>, | |
"_tag" | |
> | |
export function taggedEnum< | |
A extends Record<string, Record<string, any>>, | |
>(): TaggedEnumConstructor<A> { | |
return Data.tagged as any | |
} | |
export type InferTaggedEnum<A extends TaggedEnumConstructor<any>> = | |
A extends TaggedEnumConstructor<infer T> | |
? { | |
[K in keyof T]: Data.Data< | |
Simplify<Readonly<T[K]> & { readonly _tag: K }> | |
> | |
}[keyof T] | |
: never | |
// ==================== | |
// ADT | |
// ==================== | |
export type ADT<A extends Record<string, Record<string, any>>> = { | |
[K in keyof A]: Data.Data<Simplify<Readonly<A[K]> & { readonly _tag: K }>> | |
}[keyof A] | |
export namespace ADT { | |
type Tag<A extends { _tag: string }> = A["_tag"] | |
type Value<A extends { _tag: string }, K extends A["_tag"]> = Omit< | |
Extract<A, { _tag: K }>, | |
"_tag" | keyof Data.Case | |
> extends infer T | |
? {} extends T | |
? void | |
: T | |
: never | |
type Result<A extends { _tag: string }, K extends A["_tag"]> = Extract< | |
A, | |
{ _tag: K } | |
> | |
export interface Constructor { | |
readonly A: unknown | |
readonly B: unknown | |
readonly C: unknown | |
readonly D: unknown | |
} | |
type Kind< | |
F extends Constructor, | |
A = unknown, | |
B = unknown, | |
C = unknown, | |
D = unknown, | |
> = F extends { | |
adt: { _tag: string } | |
} | |
? (F & { | |
readonly A: A | |
readonly B: B | |
readonly C: C | |
readonly D: D | |
})["adt"] | |
: never | |
export type Constructor1<F extends Constructor> = <K extends Tag<Kind<F>>>( | |
tag: K, | |
) => <A>(value: Value<Kind<F, A>, K>) => Result<Kind<F, A>, K> | |
export type Constructor2<F extends Constructor> = <K extends Tag<Kind<F>>>( | |
tag: K, | |
) => <A, B>(value: Value<Kind<F, A, B>, K>) => Result<Kind<F, A, B>, K> | |
export type Constructor3<F extends Constructor> = <K extends Tag<Kind<F>>>( | |
tag: K, | |
) => <A, B, C>( | |
value: Value<Kind<F, A, B, C>, K>, | |
) => Result<Kind<F, A, B, C>, K> | |
export type Constructor4<F extends Constructor> = <K extends Tag<Kind<F>>>( | |
tag: K, | |
) => <A, B = unknown, C = unknown, D = unknown>( | |
value: Value<Kind<F, A, B, C, D>, K>, | |
) => Result<Kind<F, A, B, C, D>, K> | |
} | |
export function adt<A extends ADT.Constructor>(): ADT.Constructor4<A> { | |
return Data.tagged as any | |
} | |
export function adt1<A extends ADT.Constructor>(): ADT.Constructor1<A> { | |
return Data.tagged as any | |
} | |
export function adt2<A extends ADT.Constructor>(): ADT.Constructor2<A> { | |
return Data.tagged as any | |
} | |
export function adt3<A extends ADT.Constructor>(): ADT.Constructor3<A> { | |
return Data.tagged as any | |
} |
This file contains hidden or 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
import { taggedEnum } from "./adt" | |
export const HttpError = taggedEnum<{ | |
BadRequest: {} | |
NotFound: { path: string } | |
InternalServerError: { message: string } | |
}>() |
This file contains hidden or 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
import { ADT, InferTaggedEnum, adt1, adt2, taggedEnum } from "./adt" | |
// definition | |
type Option<A> = ADT<{ | |
Some: { value: A } | |
None: {} | |
}> | |
interface OptionC extends ADT.Constructor { | |
adt: Option<this["A"]> | |
} | |
const Option = adt1<OptionC>() | |
// usage | |
const some = Option("Some")({ value: 1 }) | |
const none = Option("None")() | |
console.log(some, none) | |
// ==== | |
// either definition | |
type Either<E, A> = ADT<{ | |
Left: { left: E } | |
Right: { right: A } | |
}> | |
interface EitherC extends ADT.Constructor { | |
adt: Either<this["A"], this["B"]> | |
} | |
// either usage | |
const Either = adt2<EitherC>() | |
const left = Either("Left")({ left: "error" }) | |
const right = Either("Right")({ right: "error" }) | |
console.log(left, right) | |
// ==== | |
const httpChunk = taggedEnum<{ | |
Version: { | |
method: | |
| "GET" | |
| "POST" | |
| "PUT" | |
| "DELETE" | |
| "PATCH" | |
| "HEAD" | |
| "OPTIONS" | |
| "TRACE" | |
major: number | |
minor: number | |
} | |
Header: { | |
key: string | |
value: string | |
} | |
Body: { | |
contents: any | |
} | |
}>() | |
export type HttpChunk = InferTaggedEnum<typeof httpChunk> | |
export const version = httpChunk("Version")({ | |
method: "GET", | |
major: 1, | |
minor: 1, | |
}) | |
console.log(version) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment