From c27ef38741d24f4eb1556625c8cb29687ea45ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikuro=E3=81=95=E3=81=84=E3=81=AA?= Date: Thu, 25 Apr 2024 07:14:11 +0900 Subject: [PATCH] feat: Add loop, while and foreach for CatT (#179) --- src/cat.ts | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/cat.ts b/src/cat.ts index 49eda515..763f2e16 100644 --- a/src/cat.ts +++ b/src/cat.ts @@ -5,7 +5,10 @@ * * @packageDocumentation */ +import { type ControlFlow, isBreak } from "./control-flow.ts"; import type { Get1, Hkt1 } from "./hkt.ts"; +import { foldL } from "./list.ts"; +import { List } from "./list.ts"; import { fromProjection as eqFromProjection } from "./type-class/eq.ts"; import type { Monad } from "./type-class/monad.ts"; import { fromProjection as ordFromProjection } from "./type-class/ord.ts"; @@ -88,6 +91,42 @@ export interface CatT { computation: (ctx: CTX) => Get1, ) => CatT; + /** + * Runs a looping computation while it returns `Continue`. + * + * @param initState - An initial state. + * @param body - A computation to run. + * @returns A new `CatT` with modified environment. + */ + readonly loop: ( + initState: S, + body: (state: S, ctx: CTX) => Get1>, + ) => CatT; + + /** + * Runs a looping computation while `cond` returns `true`. + * + * @param cond - A function to decide to continue the loop. + * @param body - A computation to run. + * @returns A new `CatT` with modified environment. + */ + readonly while: ( + cond: (ctx: CTX) => boolean, + body: (ctx: CTX) => Get1, + ) => CatT; + + /** + * Runs a looping computation with items from the list. + * + * @param iter - A list to be iterated. + * @param body - A computation to run. + * @returns A new `CatT` with modified environment. + */ + readonly foreach: ( + iter: List, + body: (item: T, ctx: CTX) => Get1, + ) => CatT; + /** * Reduces the context into a value by `fn`. * @@ -164,6 +203,38 @@ export const catT = ), )(ctx), ), + loop: ( + initialState: S, + body: (state: S, ctx: CTX) => Get1>, + ): CatT => { + const go = (state: S): Get1 => + monad.flatMap((c: CTX) => + monad.flatMap((flow: ControlFlow<[], S>): Get1 => + isBreak(flow) ? monad.pure(c) : go(flow[1]) + )(body(state, c)) + )(ctx); + return catT(monad)(go(initialState)); + }, + while: (cond, body) => { + const go = (ctx: Get1): Get1 => + monad.flatMap((c: CTX) => + cond(c) + ? monad.flatMap(() => go(ctx))(body(c)) + : monad.pure(c) + )(ctx); + return catT(monad)(go(ctx)); + }, + foreach: ( + iter: List, + body: (item: T, ctx: CTX) => Get1, + ): CatT => + catT(monad)( + foldL((acc: Get1) => (item: T) => + monad.flatMap((c: CTX) => + monad.map(() => c)(body(item, c)) + )(acc) + )(ctx)(iter), + ), finish: (fn: (ctx: CTX) => R) => monad.map(fn)(ctx), finishM: (fn) => monad.flatMap(fn)(ctx), });