Last active
August 31, 2022 22:55
-
-
Save baetheus/0572023bed2d20e3364a70bd6b62dd30 to your computer and use it in GitHub Desktop.
Working general compose with narrowing "never" on incorrect usage
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
// deno-fmt-ignore-file no-explicit-any | |
import type { Iso } from "./iso.ts"; | |
import type { Lens } from "./lens.ts"; | |
import type { Prism } from "./prism.ts"; | |
import type { Optional } from "./optional.ts"; | |
import type { Traversal } from "./traversal.ts"; | |
import * as I from "./iso.ts"; | |
import * as L from "./lens.ts"; | |
import * as P from "./prism.ts"; | |
import * as O from "./optional.ts"; | |
import * as T from "./traversal.ts"; | |
import { pipe } from "./fns.ts"; | |
export type Optic<A, B> = | |
| Iso<A, B> | |
| Lens<A, B> | |
| Prism<A, B> | |
| Optional<A, B> | |
| Traversal<A, B>; | |
// deno-fmt-ignore | |
// deno-lint-ignore no-explicit-any | |
export type Compose<Left extends Optic<any, any>, Right extends Optic<any, any>> = | |
// Iso | |
Left extends Iso<infer A, infer B> | |
? Right extends Iso<B, infer C> ? Iso<A, C> | |
: Right extends Lens<B, infer C> ? Lens<A, C> | |
: Right extends Prism<B, infer C> ? Prism<A, C> | |
: Right extends Optional<B, infer C> ? Optional<A, C> | |
: Right extends Traversal<B, infer C> ? Traversal<A, C> | |
: never | |
: Left extends Lens<infer A, infer B> | |
? Right extends Iso<B, infer C> ? Lens<A, C> | |
: Right extends Lens<B, infer C> ? Lens<A, C> | |
: Right extends Prism<B, infer C> ? Optional<A, C> | |
: Right extends Optional<B, infer C> ? Optional<A, C> | |
: Right extends Traversal<B, infer C> ? Traversal<A, C> | |
: never | |
: Left extends Prism<infer A, infer B> | |
? Right extends Iso<B, infer C> ? Prism<A, C> | |
: Right extends Lens<B, infer C> ? Optional<A, C> | |
: Right extends Prism<B, infer C> ? Prism<A, C> | |
: Right extends Optional<B, infer C> ? Optional<A, C> | |
: Right extends Traversal<B, infer C> ? Traversal<A, C> | |
: never | |
: Left extends Optional<infer A, infer B> | |
? Right extends Iso<B, infer C> ? Optional<A, C> | |
: Right extends Lens<B, infer C> ? Optional<A, C> | |
: Right extends Prism<B, infer C> ? Optional<A, C> | |
: Right extends Optional<B, infer C> ? Optional<A, C> | |
: Right extends Traversal<B, infer C> ? Traversal<A, C> | |
: never | |
: Left extends Traversal<infer A, infer B> | |
? Right extends Iso<B, infer C> ? Traversal<A, C> | |
: Right extends Lens<B, infer C> ? Traversal<A, C> | |
: Right extends Prism<B, infer C> ? Traversal<A, C> | |
: Right extends Optional<B, infer C> ? Traversal<A, C> | |
: Right extends Traversal<B, infer C> ? Traversal<A, C> | |
: never | |
: never; | |
// deno-lint-ignore no-explicit-any | |
type NarrowLeft<R> = R extends Optic<infer B, infer _> ? Optic<any, B> : never; | |
// deno-lint-ignore no-explicit-any | |
export function compose<R extends Optic<any, any>>(right: R): <L extends NarrowLeft<R>>(left: L) => Compose<L, R> { | |
return <L extends NarrowLeft<R>>(left: L): Compose<L, R>=> { | |
switch (left.tag) { | |
case "Iso": | |
switch (right.tag) { | |
case "Iso": return I.compose(right)(left) as Compose<L, R>; | |
case "Lens": return I.composeLens(right)(left) as Compose<L, R>; | |
case "Prism": return I.composePrism(right)(left) as Compose<L, R>; | |
case "Optional": return I.composeOptional(right)(left) as Compose<L, R>; | |
case "Traversal": return I.composeTraversal(right)(left) as Compose<L, R>; | |
} | |
break; | |
case "Lens": | |
switch (right.tag) { | |
case "Iso": return L.composeIso(right)(left) as Compose<L, R>; | |
case "Lens": return L.compose(right)(left) as Compose<L, R>; | |
case "Prism": return L.composePrism(right)(left) as Compose<L, R>; | |
case "Optional": return L.composeOptional(right)(left) as Compose<L, R>; | |
case "Traversal": return L.composeTraversal(right)(left) as Compose<L, R>; | |
} | |
break; | |
case "Prism": | |
switch (right.tag) { | |
case "Iso": return P.composeIso(right)(left) as Compose<L, R>; | |
case "Lens": return P.composeLens(right)(left) as Compose<L, R>; | |
case "Prism": return P.compose(right)(left) as Compose<L, R>; | |
case "Optional": return P.composeOptional(right)(left) as Compose<L, R>; | |
case "Traversal": return P.composeTraversal(right)(left) as Compose<L, R>; | |
} | |
break; | |
case "Optional": | |
switch (right.tag) { | |
case "Iso": return O.composeIso(right)(left) as Compose<L, R>; | |
case "Lens": return O.composeLens(right)(left) as Compose<L, R>; | |
case "Prism": return O.composePrism(right)(left) as Compose<L, R>; | |
case "Optional": return O.compose(right)(left) as Compose<L, R>; | |
case "Traversal": return O.composeTraversal(right)(left) as Compose<L, R>; | |
} | |
break; | |
case "Traversal": | |
switch (right.tag) { | |
case "Iso": return T.composeIso(right)(left) as Compose<L, R>; | |
case "Lens": return T.composeLens(right)(left) as Compose<L, R>; | |
case "Prism": return T.composePrism(right)(left) as Compose<L, R>; | |
case "Optional": return T.composeOptional(right)(left) as Compose<L, R>; | |
case "Traversal": return T.compose(right)(left) as Compose<L, R>; | |
} | |
break; | |
} | |
}; | |
} | |
// TEST | |
type TEST = { | |
one: number, | |
two: { | |
three: string, | |
} | |
}; | |
const test: TEST = { | |
one: 1, | |
two: { three: "three" } | |
} | |
const TT: Iso<TEST, [number, string]> = I.iso( | |
({ one, two: { three }}) => [one, three], | |
([one, three]) => ({ one, two: { three }}) | |
); | |
const tt = I.reverse(TT); | |
const two = pipe( | |
L.id<TEST>(), | |
L.prop("two"), | |
) | |
const three = pipe( | |
P.id<TEST["two"]>(), | |
P.prop("three"), | |
); | |
// Optional<TEST, string> | |
export const c = pipe( | |
TT, | |
compose(tt), | |
compose(TT), | |
compose(tt), | |
compose(two), | |
compose(three) | |
) | |
console.log(c.getOption(test)); | |
console.log(c.set("THREE")(test)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment