/** * This file contains the Free algebraic data type. Free is a data type that is * used primarily to create a Combinable for any given data structure. It is * useful when one wants to use combine things without deciding on a specific * data structure to implement. * * @experimental * @module Free * @since 2.0.0 */ import type { Kind, Out } from "./kind.ts"; import type { Combinable } from "./combinable.ts"; import { flow, pipe } from "./fn.ts"; /** * @since 2.0.0 */ export type Node = { readonly tag: "Node"; readonly value: A; }; /** * @since 2.0.0 */ export type Link = { readonly tag: "Link"; readonly first: Free; readonly second: Free; }; /** * @since 2.0.0 */ export type Free = Node | Link; /** * @since 2.0.0 */ export interface KindFree extends Kind { readonly kind: Free>; } /** * @since 2.0.0 */ export function node(value: A): Free { return { tag: "Node", value }; } /** * @since 2.0.0 */ export function link( first: Free, second: Free, ): Free { return { tag: "Link", first, second }; } /** * @since 2.0.0 */ export function isNode(ua: Free): ua is Node { return ua.tag === "Node"; } /** * @since 2.0.0 */ export function isLink(ua: Free): ua is Link { return ua.tag === "Link"; } /** * @since 2.0.0 */ export function match( onNode: (value: A) => O, onLink: (first: Free, second: Free) => O, ): (ua: Free) => O { return (ua) => { switch (ua.tag) { case "Node": return onNode(ua.value); case "Link": return onLink(ua.first, ua.second); } }; } /** * @since 2.0.0 */ export function combine( second: Free, ): (first: Free) => Free { return (first) => ({ tag: "Link", first, second }); } /** * @since 2.0.0 */ export function wrap(a: A): Free { return node(a); } /** * @since 2.0.0 */ export function map( fai: (a: A) => I, ): (ua: Free) => Free { const go: (ua: Free) => Free = match( flow(fai, node), (first, second) => link(go(first), go(second)), ); return go; } /** * @since 2.0.0 */ export function flatmap( faui: (a: A) => Free, ): (ua: Free) => Free { const go: (ua: Free) => Free = match( faui, (first, second): Free => link(go(first), go(second)), ); return go; } /** * @since 2.0.0 */ export function apply(ua: Free): (ufai: Free<(a: A) => I>) => Free { return (ufai) => pipe(ufai, flatmap(flow(map, (fn) => fn(ua)))); } /** * @since 2.0.0 */ export function fold( foldr: (value: A, accumulator: O) => O, initial: O, ): (ua: Free) => O { // :( let result = initial; const go: (ua: Free) => O = match( (value) => { result = foldr(value, result); return result; }, (first, second) => { go(first); return go(second); }, ); return go; } /** * @since 2.0.0 */ export function getCombinable(): Combinable> { return { combine }; } // TODO: Showable, Sortable, Traversable //