defmodule Quark.FixedPoint do @moduledoc ~S""" Fixed point combinators generalize the idea of a recursive function. This can be used to great effect, simplifying many definitions. For example, here is the factorial function written in terms of `y/1`: iex> fac = fn fac -> ...> fn ...> 0 -> 0 ...> 1 -> 1 ...> n -> n * fac.(n - 1) ...> end ...> end ...> factorial = y(fac) ...> factorial.(9) 362880 The resulting function will always be curried iex> import Quark.SKI, only: [s: 3] ...> one_run = y(&s/3) ...> {_, arity} = :erlang.fun_info(one_run, :arity) ...> arity 1 """ import Quark.Partial import Quark.Curry, only: [curry: 1] defdelegate fix(), to: __MODULE__, as: :y defdelegate fix(a), to: __MODULE__, as: :y @doc ~S""" The famous Y-combinator. The resulting function will always be curried. ## Examples iex> fac = fn fac -> ...> fn ...> 0 -> 0 ...> 1 -> 1 ...> n -> n * fac.(n - 1) ...> end ...> end ...> factorial = y(fac) ...> factorial.(9) 362880 """ @spec y(fun) :: fun defpartial y(fun) do (fn x -> x.(x) end).(fn y -> curry(fun).(fn arg -> y.(y).(arg) end) end) end @doc ~S""" Alan Turing's fix-point combinator. This is the call-by-value formulation. ## Examples iex> fac = fn fac -> ...> fn ...> 0 -> 0 ...> 1 -> 1 ...> n -> n * fac.(n - 1) ...> end ...> end ...> factorial = turing(fac) ...> factorial.(9) 362880 """ @spec turing(fun) :: fun defpartial turing(fun), do: turing_inner().(turing_inner()).(fun) defpartialp turing_inner(x, y) do cx = curry(x) cy = curry(y) cy.(&(cx.(cx).(cy).(&1))) end @doc ~S""" A [normal order](https://wikipedia.org/wiki/Evaluation_strategy#Normal_order) fixed point. ## Examples iex> fac = fn fac -> ...> fn ...> 0 -> 0 ...> 1 -> 1 ...> n -> n * fac.(n - 1) ...> end ...> end ...> factorial = z(fac) ...> factorial.(9) 362880 """ @spec z(fun, any) :: fun defpartial z(g, v), do: g.(z(g)).(v) end