Skip to content

Instantly share code, notes, and snippets.

@baetheus
Last active July 22, 2025 22:54
Show Gist options
  • Select an option

  • Save baetheus/5957cbd6886e740fa94b77277095d05a to your computer and use it in GitHub Desktop.

Select an option

Save baetheus/5957cbd6886e740fa94b77277095d05a to your computer and use it in GitHub Desktop.
Folderator

Ok, so I thought I had dropped this question for the night but then while I was just cleaning up formatting I realized that there was a little bit of a pattern here:

type Tag = LensTag | AffineTag | FoldTag;

export type Viewer<T extends Tag, S, A> = {
  readonly tag: T;
  readonly view: (s: S) => $<ToURI<T>, [A, never, never]>;
};

export type Modifier<S, A> = {
  readonly modify: (modifyFn: (a: A) => A) => (s: S) => S;
};

export type Reviewer<S, A> = {
  readonly review: (a: A) => S;
};

export type Optic<T extends Tag, S, A> = Viewer<T, S, A> & Modifier<S, A>;

export type Lens<S, A> = Optic<LensTag, S, A>;
export type Iso<S, A> = Lens<S, A> & Reviewer<S, A>;

export type AffineFold<S, A> = Optic<AffineTag, S, A>;
export type Prism<S, A> = AffineFold<S, A> & Reviewer<S, A>;

export type Fold<S, A> = Optic<FoldTag, S, A>;
  • Iso = Lens + Reviewer
  • Prism = AffineFold + Reviewer
  • ??? = Fold + Reviewer

So I decided to just implement a constructor for Fold + Reviewer and see what would happen..

/**
 * Hacking around with nonsense
 */

export type Folderator<S, A> = Fold<S, A> & Reviewer<S, A>;

export function folderator<S, A>(
  view: (s: S) => ReadonlyArray<A>,
  review: (a: A) => S,
  modify: (modifyFn: (a: A) => A) => (s: S) => S,
): Folderator<S, A> {
  return { tag: FoldTag, view, review, modify };
}

const _array: Folderator<ReadonlyArray<any>, any> = folderator(
  identity,
  A.of,
  A.map,
);

function arrFold<A>(): Folderator<ReadonlyArray<A>, A> {
  return _array;
}

const _set: Folderator<ReadonlySet<any>, any> = folderator(
  Array.from,
  S.of,
  S.map,
);

function setFold<A>(): Folderator<ReadonlySet<A>, A> {
  return _set;
}

After implementing folderator for array and set, I saw that the general "folderator" is pretty straightforward

function retraverse<U extends Kind>(
  T: Traversable<U>,
  of: <A, B = never, C = never, D = unknown, E = never>(
    a: A,
  ) => $<U, [A, B, C], [D], [E]>,
): <A, B = never, C = never, D = unknown, E = never>() => Folderator<
  $<U, [A, B, C], [D], [E]>,
  A
> {
  return () =>
    folderator(
      T.reduce((as, a) => [...as, a], A.empty()),
      of,
      T.map,
    );
}

So.. I'll probably look around for prior art for Fold + Reviewer (of which there likely isn't much since neither Larhooven lenses nor Profunctor optics construct the pattern with view/modify/review). If I find some cool, but otherwise I'll likely come up with a better name than folderator and work on a better compose that incorporates Reviewer composition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment