-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: Add natural transformations and profunctors (#48)
- Loading branch information
1 parent
524b5c3
commit cfc1652
Showing
16 changed files
with
422 additions
and
1 deletion.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import type { Get1, Hkt1, Hkt3 } from "./hkt.js"; | ||
import { compose, id } from "./func.js"; | ||
|
||
import type { Contravariant } from "./type-class/variance.js"; | ||
|
||
export interface Coyoneda<F, B, A> { | ||
readonly hom: (a: A) => B; | ||
readonly map: Get1<F, B>; | ||
} | ||
export interface CoyonedaConstructor<A> { | ||
<B>(hom: (a: A) => B): <F>(map: Get1<F, B>) => Coyoneda<F, B, A>; | ||
} | ||
export const coyoneda = | ||
<A>(): CoyonedaConstructor<A> => | ||
<B>(hom: (a: A) => B) => | ||
<F>(map: Get1<F, B>): Coyoneda<F, B, A> => ({ | ||
hom, | ||
map, | ||
}); | ||
|
||
export const lift = <F, A>(fa: Get1<F, A>): Coyoneda<F, A, A> => coyoneda<A>()(id)(fa); | ||
|
||
export const lower = | ||
<F extends Hkt1>(contra: Contravariant<F>) => | ||
<B, A>(coy: Coyoneda<F, B, A>): Get1<F, A> => | ||
contra.contraMap(coy.hom)(coy.map); | ||
|
||
export const hoist = | ||
<F, G>(nat: <A>(fa: Get1<F, A>) => Get1<G, A>) => | ||
<B, A>(coy: Coyoneda<F, B, A>): Coyoneda<G, B, A> => | ||
coyoneda<A>()(coy.hom)(nat(coy.map)); | ||
|
||
export const contraMap = | ||
<T, U>(fn: (t: T) => U) => | ||
<F, B>(coy: Coyoneda<F, B, U>): Coyoneda<F, B, T> => { | ||
const { hom, map } = coy; | ||
return { hom: compose(hom)(fn), map }; | ||
}; | ||
|
||
export interface CoyonedaHkt extends Hkt3 { | ||
readonly type: Coyoneda<this["arg3"], this["arg2"], this["arg1"]>; | ||
} | ||
|
||
export const contravariant: Contravariant<CoyonedaHkt> = { contraMap }; |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import type { Category3Monoid } from "./type-class/category.js"; | ||
import type { GetHktA1 } from "./hkt.js"; | ||
import type { Monad1 } from "./type-class/monad.js"; | ||
|
||
export interface Kleisli<M, A, B> { | ||
readonly runKleisli: (a: A) => GetHktA1<M, B>; | ||
} | ||
|
||
declare const kleisliNominal: unique symbol; | ||
export type KleisliHktKey = typeof kleisliNominal; | ||
|
||
declare module "./hkt.js" { | ||
interface HktDictA3<A1, A2, A3> { | ||
[kleisliNominal]: Kleisli<A1, A2, A3>; | ||
} | ||
} | ||
|
||
export const category = <M>(monad: Monad1<M>): Category3Monoid<KleisliHktKey, M> => ({ | ||
identity: <A>() => ({ | ||
runKleisli: monad.pure<A>, | ||
}), | ||
compose: | ||
({ runKleisli: f }) => | ||
({ runKleisli: g }) => ({ | ||
runKleisli: (a) => monad.flatMap(g)(f(a)), | ||
}), | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import type { Get1, Get2, Hkt1, Hkt2 } from "./hkt.js"; | ||
import { Profunctor, fnPro, leftMap } from "./type-class/profunctor.js"; | ||
import { Settable, taintedDot, untaintedDot } from "./type-class/settable.js"; | ||
|
||
import type { Applicative } from "./type-class/applicative.js"; | ||
import type { Apply } from "./type-class/apply.js"; | ||
import type { Bifunctor } from "./type-class/bifunctor.js"; | ||
import type { Choice } from "./type-class/choice.js"; | ||
import type { Contravariant } from "./type-class/variance.js"; | ||
import type { Functor } from "./type-class/functor.js"; | ||
import type { IdentityHkt } from "./identity.js"; | ||
|
||
export type LensLike<F, S, T, A, B> = (outer: (a: A) => Get1<F, B>) => (s: S) => Get1<F, T>; | ||
export type LensLikeSimple<F, S, A> = LensLike<F, S, S, A, A>; | ||
|
||
export type Over<P, F, S, T, A, B> = (g: Get2<P, A, Get1<F, B>>) => (s: S) => Get1<F, T>; | ||
export type OverSimple<P, F, S, A> = Over<P, F, S, S, A, A>; | ||
|
||
export interface Lens<S, T, A, B> { | ||
<F extends Hkt1>(functor: Functor<F>): LensLike<F, S, T, A, B>; | ||
} | ||
export type LensSimple<S, A> = Lens<S, S, A, A>; | ||
|
||
export interface Traversal<S, T, A, B> { | ||
<F extends Hkt1>(applicative: Applicative<F>): LensLike<F, S, T, A, B>; | ||
} | ||
export type TraversalSimple<S, A> = Traversal<S, S, A, A>; | ||
|
||
export interface Traversal1<S, T, A, B> { | ||
<F extends Hkt1>(applicative: Apply<F>): LensLike<F, S, T, A, B>; | ||
} | ||
export type Traversal1Simple<S, A> = Traversal1<S, S, A, A>; | ||
|
||
export interface Setting<P, S, T, A, B> { | ||
(g: Get2<P, A, Get1<IdentityHkt, B>>): (s: S) => Get1<IdentityHkt, T>; | ||
} | ||
export type SettingSimple<P, S, A> = Setting<P, S, S, A, A>; | ||
|
||
export interface Setter<S, T, A, B> { | ||
<F extends Hkt1>(settable: Settable<F>): LensLike<F, S, T, A, B>; | ||
} | ||
export type SetterSimple<S, A> = Setter<S, S, A, A>; | ||
|
||
export interface Iso<S, T, A, B> { | ||
<P extends Hkt2, F extends Hkt1>(pro: Profunctor<P>, functor: Functor<F>): ( | ||
g: Get2<P, A, Get1<F, B>>, | ||
) => Get2<P, S, Get1<F, T>>; | ||
} | ||
export type IsoSimple<S, A> = Iso<S, S, A, A>; | ||
|
||
export interface Review<T, B> { | ||
<P extends Hkt2, F extends Hkt1>( | ||
choice: Choice<P>, | ||
bi: Bifunctor<P>, | ||
settable: Settable<F>, | ||
): OpticSimple<P, F, T, B>; | ||
} | ||
|
||
export interface Prism<S, T, A, B> { | ||
<P extends Hkt2, F extends Hkt1>(choice: Choice<P>, app: Applicative<F>): ( | ||
g: Get2<P, A, Get1<F, B>>, | ||
) => Get2<P, S, Get1<F, T>>; | ||
} | ||
export type PrismSimple<S, A> = Prism<S, S, A, A>; | ||
|
||
export interface Equality<K1, K2, S extends K1, T extends K2, A extends K1, B extends K2> { | ||
<K3, P, F>(setter: (k1: K1) => (k3: K3) => void, getter: (k2: K2) => K3): ( | ||
g: Get2<P, A, Get1<F, B>>, | ||
) => Get2<P, S, Get1<F, T>>; | ||
} | ||
export type EqualitySimple<K, S extends K, A extends K> = Equality<K, K, S, S, A, A>; | ||
export type As<K, A extends K> = EqualitySimple<K, A, A>; | ||
|
||
export interface Getter<S, A> { | ||
<F extends Hkt1>(contra: Contravariant<F>, functor: Functor<F>): ( | ||
g: (a: A) => Get1<F, A>, | ||
) => (s: S) => Get1<F, S>; | ||
} | ||
|
||
export interface Fold<S, A> { | ||
<F extends Hkt1>(contra: Contravariant<F>, app: Applicative<F>): ( | ||
g: (a: A) => Get1<F, A>, | ||
) => (s: S) => Get1<F, S>; | ||
} | ||
export interface Fold1<S, A> { | ||
<F extends Hkt1>(contra: Contravariant<F>, app: Apply<F>): ( | ||
g: (a: A) => Get1<F, A>, | ||
) => (s: S) => Get1<F, S>; | ||
} | ||
|
||
export interface Optic<P, F, S, T, A, B> { | ||
(g: Get2<P, A, Get1<F, B>>): Get2<P, S, Get1<F, T>>; | ||
} | ||
export type OpticSimple<P, F, S, A> = Optic<P, F, S, S, A, A>; | ||
|
||
export interface Optical<P, Q, F, S, T, A, B> { | ||
(g: Get2<P, A, Get1<F, B>>): Get2<Q, S, Get1<F, T>>; | ||
} | ||
export type OpticalSimple<P, Q, F, S, A> = Optical<P, Q, F, S, S, A, A>; | ||
|
||
export const sets = | ||
<P extends Hkt2, Q extends Hkt2, F extends Hkt1>( | ||
proP: Profunctor<P>, | ||
proQ: Profunctor<Q>, | ||
settable: Settable<F>, | ||
) => | ||
<S, T, A, B>(f: (pab: Get2<P, A, B>) => Get2<Q, S, T>): Optical<P, Q, F, S, T, A, B> => | ||
(g) => | ||
taintedDot(settable)(proQ)(f(untaintedDot(settable)(proP)(g))); | ||
|
||
export const mapped = | ||
<F extends Hkt1, A, B>(functor: Functor<F>): Setter<Get1<F, A>, Get1<F, B>, A, B> => | ||
(settable) => | ||
sets(fnPro, fnPro, settable)(functor.map); | ||
|
||
export const contraMapped = | ||
<F extends Hkt1, A, B>(contra: Contravariant<F>): Setter<Get1<F, B>, Get1<F, A>, A, B> => | ||
(settable) => | ||
sets(fnPro, fnPro, settable)(contra.contraMap); | ||
|
||
export const argument = | ||
<P extends Hkt2, A, B, R>(pro: Profunctor<P>): Setter<Get2<P, B, R>, Get2<P, A, R>, A, B> => | ||
(settable) => | ||
sets(fnPro, fnPro, settable)(leftMap(pro)); |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { Category, pipe } from "./category.js"; | ||
import type { Get2, Hkt2 } from "src/hkt.js"; | ||
|
||
import type { Tuple } from "../tuple.js"; | ||
|
||
export interface Arrow<A extends Hkt2> extends Category<A> { | ||
readonly arr: <B, C>(fn: (b: B) => C) => Get2<A, B, C>; | ||
readonly split: <B1, C1>( | ||
arrow1: Get2<A, B1, C1>, | ||
) => <B2, C2>(arrow2: Get2<A, B2, C2>) => Get2<A, Tuple<B1, B2>, Tuple<C1, C2>>; | ||
} | ||
|
||
export const first = | ||
<A extends Hkt2>(a: Arrow<A>) => | ||
<B, C, D>(arrow: Get2<A, B, C>): Get2<A, Tuple<B, D>, Tuple<C, D>> => | ||
a.split(arrow)(a.identity<D>()); | ||
|
||
export const second = | ||
<A extends Hkt2>(a: Arrow<A>) => | ||
<B, C, D>(arrow: Get2<A, B, C>): Get2<A, Tuple<D, B>, Tuple<D, C>> => | ||
a.split(a.identity<D>())(arrow); | ||
|
||
export const fanOut = | ||
<A extends Hkt2>(a: Arrow<A>) => | ||
<B, C1>(arrow1: Get2<A, B, C1>) => | ||
<C2>(arrow2: Get2<A, B, C2>): Get2<A, B, Tuple<C1, C2>> => | ||
pipe(a)(a.arr((b: B): Tuple<B, B> => [b, b]))(a.split(arrow1)(arrow2)); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { Get2 } from "../hkt.js"; | ||
|
||
export interface Bifunctor<P> { | ||
readonly biMap: <A, B>( | ||
first: (a: A) => B, | ||
) => <C, D>(second: (c: C) => D) => (curr: Get2<P, A, C>) => Get2<P, B, D>; | ||
} |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import type { Get2, Hkt2 } from "src/hkt.js"; | ||
|
||
import type { Profunctor } from "./profunctor.js"; | ||
import type { Result } from "src/result.js"; | ||
|
||
export interface Choice<P extends Hkt2> extends Profunctor<P> { | ||
readonly left: <A, B, C>(curr: Get2<P, A, B>) => Get2<P, Result<A, C>, Result<B, C>>; | ||
readonly right: <A, B, C>(curr: Get2<P, A, B>) => Get2<P, Result<C, A>, Result<C, B>>; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import type { Get1, Hkt1 } from "src/hkt.js"; | ||
|
||
import type { Functor } from "./functor.js"; | ||
import type { Monad } from "./monad.js"; | ||
|
||
export interface Distributive<G extends Hkt1> extends Functor<G> { | ||
readonly distribute: <F extends Hkt1>( | ||
functor: Functor<F>, | ||
) => <A>(fga: Get1<F, Get1<G, A>>) => Get1<G, Get1<F, A>>; | ||
} | ||
|
||
export const collect = | ||
<G extends Hkt1>(dist: Distributive<G>) => | ||
<F extends Hkt1>(functor: Functor<F>) => | ||
<A, B>(f: (a: A) => Get1<G, B>) => | ||
(fa: Get1<F, A>): Get1<G, Get1<F, B>> => | ||
dist.distribute(functor)(functor.map(f)(fa)); | ||
|
||
export const distributeM = | ||
<G extends Hkt1>(dist: Distributive<G>) => | ||
<M extends Hkt1>(monad: Monad<M>) => | ||
<A>(mga: Get1<M, Get1<G, A>>): Get1<G, Get1<M, A>> => | ||
dist.distribute(monad)(mga); | ||
|
||
export const collectM = | ||
<G extends Hkt1>(dist: Distributive<G>) => | ||
<M extends Hkt1>(monad: Monad<M>) => | ||
<A, B>(f: (a: A) => Get1<G, B>) => | ||
(fa: Get1<M, A>): Get1<G, Get1<M, B>> => | ||
dist.distribute(monad)(monad.map(f)(fa)); | ||
|
||
export const contraverse = | ||
<G extends Hkt1>(dist: Distributive<G>) => | ||
<F extends Hkt1>(functor: Functor<F>) => | ||
<A, B>(f: (fa: Get1<F, A>) => B) => | ||
(fga: Get1<F, Get1<G, A>>): Get1<G, B> => | ||
dist.map(f)(dist.distribute(functor)(fga)); | ||
|
||
export const coMapM = | ||
<G extends Hkt1>(dist: Distributive<G>) => | ||
<M extends Hkt1>(monad: Monad<M>) => | ||
<A, B>(f: (ma: Get1<M, A>) => B) => | ||
(mga: Get1<M, Get1<G, A>>): Get1<G, B> => | ||
dist.map(f)(distributeM(dist)(monad)(mga)); |
Oops, something went wrong.