Skip to content

Commit

Permalink
feat: modify Newtype and add Nest
Browse files Browse the repository at this point in the history
Added a Nest type to kind.ts that allows for easier nesting of kinds. Modified
Newtype to use kind.ts#Hold instead of a fake object/accessor pattern. Also
played with various ideas in contrib and examples.
  • Loading branch information
baetheus committed Mar 10, 2024
1 parent 32e5293 commit 40a0012
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 35 deletions.
21 changes: 21 additions & 0 deletions contrib/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { In, Kind, Out } from "../kind.ts";
import type { Flatmappable } from "../flatmappable.ts";

import { todo } from "../fn.ts";

export interface KindGenerator extends Kind {
readonly kind: Generator<Out<this, 0>, Out<this, 1>, In<this, 0>>;
}

export const FlatmappableGenerator: Flatmappable<KindGenerator> = {
wrap: (a) => {
const ret = function* () {
yield a;
return undefined;
};
return ret;
},
map: todo(),
apply: todo(),
flatmap: todo(),
};
18 changes: 8 additions & 10 deletions contrib/most.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Scheduler, Stream } from "npm:@most/[email protected]";
import type { $, In, InOut, Kind, Out } from "../kind.ts";
import type { $, Kind, Nest, Out } from "../kind.ts";
import type { Wrappable } from "../wrappable.ts";
import type { Mappable } from "../mappable.ts";
import type { Applicable } from "../applicable.ts";
Expand Down Expand Up @@ -76,7 +76,12 @@ export const map: Mappable<KindStream>["map"] = M.map;

export const apply: Applicable<KindStream>["apply"] = M.ap;

export const flatmap: Flatmappable<KindStream>["flatmap"] = M.chain;
export const flatmap: Flatmappable<KindStream>["flatmap"] = (faui) => (ua) =>
pipe(
ua,
M.map(faui),
M.switchLatest,
);

export const WrappableStream: Wrappable<KindStream> = { wrap };

Expand All @@ -98,14 +103,7 @@ export const bindTo = createBindTo(FlatmappableStream);
export const tap = createTap(FlatmappableStream);

export interface TransformStream<U extends Kind> extends Kind {
readonly kind: Stream<
$<
U,
[Out<this, 0>, Out<this, 1>, Out<this, 2>],
[In<this, 0>],
[InOut<this, 0>]
>
>;
readonly kind: Stream<Nest<U, this>>;
}

export function transformStream<U extends Kind>(
Expand Down
2 changes: 1 addition & 1 deletion examples/fetch_archives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as D from "../decoder.ts";
import * as E from "../either.ts";
import * as S from "../schemable.ts";
import * as AE from "../async_either.ts";
import * as O from "../optics.ts";
import * as O from "../optic.ts";
import * as J from "../json_schema.ts";
import { flow, pipe } from "../fn.ts";

Expand Down
92 changes: 92 additions & 0 deletions examples/profunctor_optics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// deno-lint-ignore-file no-explicit-any
//
import type { $, Hold, In, Kind, Out } from "../kind.ts";
import type { Pair } from "../pair.ts";
import type { Mappable } from "../mappable.ts";

import * as O from "../option.ts";
import * as F from "../fn.ts";
import * as P from "../pair.ts";
import { flow, pipe, todo } from "../fn.ts";

export interface Profunctor<U extends Kind> {
readonly dimap: <D, L, A, I, B = never, C = never, E = unknown>(
fld: (l: L) => D,
fai: (a: A) => I,
) => (ua: $<U, [A, B, C], [D], [E]>) => $<U, [I, B, C], [L], [E]>;
}

export interface Forget<R, A, B> extends Hold<B> {
readonly runForget: (a: A) => R;
}

export interface KindForget extends Kind {
readonly kind: Forget<Out<this, 1>, In<this, 0>, Out<this, 0>>;
}

export const ProfunctorForget: Profunctor<KindForget> = {
dimap: (fld, _fai) => (ua) => ({
runForget: flow(fld, ua.runForget),
}),
};

export interface Star<U extends Kind, X, A> {
readonly runStar: <B = never, C = never, D = unknown, E = unknown>(
x: X,
) => $<U, [A, B, C], [D], [E]>;
}

export interface KindStar<U extends Kind> extends Kind {
readonly kind: Star<U, In<this, 0>, Out<this, 0>>;
}

export function profunctorStar<U extends Kind>(
M: Mappable<U>,
): Profunctor<KindStar<U>> {
return {
dimap: (fld, fai) => (ua) => ({
// This should work but we need to do some type spelunking
runStar: (flow(fld, ua.runStar, M.map(fai))) as any,
}),
};
}

export interface Strong<U extends Kind> extends Profunctor<U> {
readonly first: <
A,
X = never,
B = never,
C = never,
D = unknown,
E = unknown,
>(
ua: $<U, [A, B, C], [D], [E]>,
) => $<U, [Pair<A, X>, B, C], [Pair<D, X>], [E]>;
readonly second: <
A,
X = never,
B = never,
C = never,
D = unknown,
E = unknown,
>(
ua: $<U, [A, B, C], [D], [E]>,
) => $<U, [Pair<X, A>, B, C], [Pair<X, D>], [E]>;
}

export function strong<U extends Kind>({ dimap }: Profunctor<U>): Strong<U> {
return {
dimap,
first: dimap(P.getFirst, (a) => P.pair(a, null as any)),
second: dimap(P.getSecond, (a) => P.pair(null as any, a)),
};
}

export const ProfunctorStarFn: Profunctor<KindStar<F.KindFn>> = profunctorStar(
F.MappableFn,
);
export const StrongStarFn: Strong<KindStar<F.KindFn>> = strong(
ProfunctorStarFn,
);

// Hmm
39 changes: 39 additions & 0 deletions examples/tree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { $, Kind, Out } from "../kind.ts";
import type { Option } from "../option.ts";

import * as O from "../option.ts";
import * as A from "../array.ts";
import { pipe } from "../fn.ts";

export type Tree<A> = {
readonly value: Option<A>;
readonly forest: Forest<A>;
};

export type Forest<A> = ReadonlyArray<Tree<A>>;

// deno-lint-ignore no-explicit-any
export type AnyTree = Tree<any>;

export type Type<U> = U extends Tree<infer A> ? A : never;

export interface KindTree extends Kind {
readonly kind: Tree<Out<this, 0>>;
}

export function tree<A>(value: Option<A>, forest: Forest<A> = []): Tree<A> {
return { value, forest };
}

export function init<A = never>(forest: Forest<A> = []): Tree<A> {
return { value: O.none, forest };
}

export function wrap<A>(value: A, forest: Forest<A> = []): Tree<A> {
return { value: O.some(value), forest };
}

export function map<A, I>(fai: (a: A) => I): (ua: Tree<A>) => Tree<I> {
const mapValue = O.map(fai);
return (ua) => tree(mapValue(ua.value), pipe(ua.forest, A.map(map(fai))));
}
14 changes: 12 additions & 2 deletions kind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ export interface Fix<A> extends Kind {
/**
* Create a scoped symbol for use with Hold.
*/
const HoldSymbol = Symbol("Hold");
type HoldSymbol = typeof HoldSymbol;
declare const HoldSymbol: unique symbol;

/**
* The Hold interface allows one to trick the typescript compiler into holding
Expand All @@ -130,6 +129,17 @@ export interface Hold<A> {
readonly [HoldSymbol]?: A;
}

/**
* Some Flatmappable Transform kinds only have one out type, in those cases we
* use this Nest type to make the transform kind cleaner.
*/
export type Nest<U extends Kind, S extends Substitutions> = $<
U,
[Out<S, 0>, Out<S, 1>, Out<S, 2>],
[In<S, 0>],
[InOut<S, 0>]
>;

/**
* Spread the keys of a struct union into a single struct.
*/
Expand Down
43 changes: 22 additions & 21 deletions newtype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,8 @@ import { fromPredicate } from "./option.ts";
import { iso as _iso, prism as _prism } from "./optic.ts";
import { identity, unsafeCoerce } from "./fn.ts";

/**
* These are phantom types used by Newtype to both identify and distinquish
* between a Newtype and its representation value.
*/
declare const Brand: unique symbol;
declare const Value: unique symbol;
declare const BrandSymbol: unique symbol;
declare const ValueSymbol: unique symbol;

/**
* Create a branded type from an existing type. The branded
Expand All @@ -45,8 +41,8 @@ declare const Value: unique symbol;
*
* type Integer = Newtype<'Integer', number>;
*
* const int = 1 as unknown as Integer;
* const num = 1;
* const int = 1 as Integer;
* const num = 1 as number;
*
* declare function addOne(n: Integer): number;
*
Expand All @@ -56,7 +52,18 @@ declare const Value: unique symbol;
*
* @since 2.0.0
*/
export type Newtype<B, A> = { readonly [Brand]: B; readonly [Value]: A };
export type Newtype<B, A> = A & {
readonly [ValueSymbol]: A;
readonly [BrandSymbol]: B;
};

/**
* A type alias for Newtype<any, any> that is useful when constructing
* Newtype related runtime instances.
*
* @since 2.0.0
*/
export type AnyNewtype = Newtype<unknown, unknown>;

/**
* Extracts the inner type value from a Newtype.
Expand All @@ -72,15 +79,9 @@ export type Newtype<B, A> = { readonly [Brand]: B; readonly [Value]: A };
*
* @since 2.0.0
*/
export type ToValue<T extends Newtype<unknown, unknown>> = T[typeof Value];

/**
* A type alias for Newtype<any, any> that is useful when constructing
* Newtype related runtime instances.
*
* @since 2.0.0
*/
export type AnyNewtype = Newtype<unknown, unknown>;
export type ToValue<T extends AnyNewtype> = T extends Newtype<infer _, infer A>
? A
: never;

/**
* Retype an existing Comparable from an inner type to a Newtype.
Expand All @@ -100,7 +101,7 @@ export type AnyNewtype = Newtype<unknown, unknown>;
export function getComparable<T extends AnyNewtype>(
eq: Comparable<ToValue<T>>,
): Comparable<T> {
return eq as Comparable<T>;
return eq as unknown as Comparable<T>;
}

/**
Expand All @@ -121,7 +122,7 @@ export function getComparable<T extends AnyNewtype>(
export function getSortable<T extends AnyNewtype>(
ord: Sortable<ToValue<T>>,
): Sortable<T> {
return ord as Sortable<T>;
return ord as unknown as Sortable<T>;
}

/**
Expand Down Expand Up @@ -227,6 +228,6 @@ export function prism<T extends AnyNewtype>(
): Prism<ToValue<T>, T> {
return _prism<ToValue<T>, T>(
fromPredicate(predicate) as (s: ToValue<T>) => Option<T>,
identity,
identity as (a: T) => ToValue<T>,
);
}
2 changes: 1 addition & 1 deletion optic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,7 @@ export function traverse<T extends Kind>(
first: Optic<U, S, $<T, [A, B, C], [D], [E]>>,
) => Optic<Align<U, FoldTag>, S, A> {
return compose(fold(
T.fold((as, a) => [...as, a], A.init()),
T.fold((as, a) => [...as, a], (<A>(): A[] => [])()),
T.map,
));
}
Expand Down

0 comments on commit 40a0012

Please sign in to comment.