A fixed-point combinator is a higher order function \$\mathrm{fix}\$ that returns the fixed point of its argument function. If the function \$f\$ has one or more fixed points, then $$\mathrm{fix} f=f(\mathrm{fix} f).$$ The combinator \$Y\$ has such properties. Encoded in lambda calculus: $$Y=\lambda f.(\lambda x.f(x x))\ (\lambda x.f (x x))$$ You can extend a fixed-point combinator to find the fixed point of the \$i\$-th function out of \$n\$ given functions. $$ \mathrm{fix}_{i,n}f_1\dots f_n=f_i(\mathrm{fix}_{1,n}f_1\dots f_n)\dots(\mathrm{fix}_{n,n}f_1\dots f_n) $$ As an extension to the \$Y\$ combinator:
\begin{alignat*}{2} Y_{i,n}=\lambda f_1\dots f_n.&((\lambda x_1\dots x_n.f_i&&(x_1x_1\dots x_n)\\ & && \dots\\ & && (x_nx_1...x_n)))\\ &((\lambda x_1\dots x_n.f_1&&(x_1x_1\dots x_n)\\ & && \dots\\ & && (x_nx_1...x_n)))\\ &\dots\\ &((\lambda x_1\dots x_n.f_n&&(x_1x_1\dots x_n)\\ & && \dots\\ & && (x_nx_1...x_n))) \end{alignat*}
Example: \begin{alignat*}{3} Y_{1,1}&=Y && &&&\\ Y_{1,2}&=\lambda f_1f_2.&&((\lambda x_1x_2.f_1&&&(x_1x_1x_2)\\ & && &&& (x_2x_1x_2))\\ & &&((\lambda x_1x_2.f_1&&&(x_1x_1x_2)\\ & && &&& (x_2x_1x_2))\\ & &&((\lambda x_1x_2.f_2&&&(x_1x_1x_2)\\ & && &&& (x_2x_1x_2)) \end{alignat*}
Your task is to write a variadic fixed-point combinator \$\mathrm{fix}^*\$ that finds and returns the fixed-points of all given functions. $$ \mathrm{fix}^*f_1\dots f_n=\langle\mathrm{fix}_{1,n}f_1\dots f_n,\dots,\mathrm{fix}_{n,n}f_1\dots f_n\rangle $$ While the details are up to you, I suggest your program accepts a list of functions and returns a list of their fixed points.
For example, take the following pseudo-Haskell functions your program should be able to solve (basically \$\mathrm{fix}_{i,2}\$):
-- even/odd using fix* with lambdas as function arguments
f = (\f g n -> if n == 0 then True else (g (n - 1)))
g = (\f g n -> if n == 0 then False else (f (n - 1)))
isEven = head $ fix* [f,g]
isOdd = tail $ fix* [f,g]
-- mod3 using fix* with lists as function arguments
h1 [h1, h2, h3] n = if n == 0 then 0 else h2 (n - 1)
h2 [h1, h2, h3] n = if n == 0 then 1 else h3 (n - 1)
h3 [h1, h2, h3] n = if n == 0 then 2 else h1 (n - 1)
mod3 = head $ fix* [h1, h2, h3]
Example (ungolfed) implementation:
Bruijn:
y* [[[0 1] <$> 0] [[1 <! ([[1 2 0]] <$> 0)]] <$> 0]
Rules:
- Use any language you like, as long as
fix*
can accept functions and return their fixed points in your preferred format code-golf
, the shortest implementation in bytes wins- You can assume a fixed point exists for every given function, you do not need to solve the halting problem
- Bonus: Subtract 10 from your byte count if your solution does not use recursion (i.e. does not use the feature of your language that's typically responsible for recursion; fixed-point combinators are allowed)
- Have fun!
Related questions:
fix*
could actually make sense! \$\endgroup\$