Last active
May 14, 2026 18:33
-
-
Save Fristi/50e819655b87cdadc61290c634691e9e to your computer and use it in GitHub Desktop.
HFix2 + HFunctor2 = recursion schemes on fix point types with arity of 2 - coded with help of Claude :)
This file contains hidden or 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
| final case class HFix2[H[_[_, _], _, _], I, O](unfix: H[[A, B] =>> HFix2[H, A, B], I, O]) | |
| /** An algebra: collapses one layer, preserving both indices */ | |
| type HAlgebra2[H[_[_, _], _, _], F[_, _]] = | |
| [I, O] => H[F, I, O] => F[I, O] | |
| trait HFunctor2[H[_[_, _], _, _]]: | |
| // cleaner standalone form | |
| def hfmap[F[_, _], G[_, _], I, O]( | |
| nt: [A, B] => F[A, B] => G[A, B] | |
| )(hfio: H[F, I, O]): H[G, I, O] | |
| def cata2[H[_[_, _], _, _], F[_, _], I, O]( | |
| alg: HAlgebra2[H, F] | |
| )(hf: HFix2[H, I, O])(using hfunctor: HFunctor2[H]): F[I, O] = | |
| def go[A, B]: HFix2[H, A, B] => F[A, B] = | |
| (fix: HFix2[H, A, B]) => | |
| alg[A, B](hfunctor.hfmap[[A, B] =>> HFix2[H, A, B], F, A, B]( | |
| [A, B] => (r: HFix2[H, A, B]) => go[A, B](r) | |
| )(fix.unfix)) | |
| go[I, O](hf) | |
| // ─── The pattern functor ──────────────────────────────────────────────────── | |
| enum ArrowF[F[_, _], I, O]: | |
| case Arr[F[_, _], I, O](f: I => O) extends ArrowF[F, I, O] | |
| case Compose[F[_, _], I, M, O]( | |
| g: F[M, O], h: F[I, M] | |
| ) extends ArrowF[F, I, O] | |
| // ─── HFunctor2 instance ───────────────────────────────────────────────────── | |
| given HFunctor2[ArrowF] with | |
| def hfmap[F[_, _], G[_, _], I, O]( | |
| nt: [A, B] => F[A, B] => G[A, B] | |
| )(h: ArrowF[F, I, O]): ArrowF[G, I, O] = | |
| h match | |
| case ArrowF.Arr(f) => ArrowF.Arr(f) | |
| case ArrowF.Compose(g, h) => ArrowF.Compose(nt(g), nt(h)) | |
| // ─── Smart constructors into HFix2 ───────────────────────────────────────── | |
| type Arrow[I, O] = HFix2[ArrowF, I, O] | |
| def arr[I, O](f: I => O): Arrow[I, O] = | |
| HFix2(ArrowF.Arr(f)) | |
| def compose[I, M, O](g: Arrow[M, O], h: Arrow[I, M]): Arrow[I, O] = | |
| HFix2(ArrowF.Compose(g, h)) | |
| // ─── Algebra: evaluate to a plain function ────────────────────────────────── | |
| val evalAlg: HAlgebra2[ArrowF, Function1] = | |
| [I, O] => (node: ArrowF[Function1, I, O]) => node match | |
| case ArrowF.Arr(f) => f | |
| case ArrowF.Compose(g, h) => g compose h | |
| val prg = compose(arr[Int, Int](_ * 2), arr[Int, Int](_ + 10)) | |
| def eval[I, O]: Arrow[I, O] => (I => O) = | |
| cata2[ArrowF, Function1, I, O](evalAlg) | |
| @main def main = println(eval(prg)(2)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment