recur_fn/lib.rs
1//! A library that provides a more flexible way to construct
2//! and extend the recursive function.
3//!
4//! The `RecurFn` trait is an abstraction of a recursive function.
5//! By accepting a function parameter `recur` as the recursion
6//! rather than recurring directly, it makes constructing an
7//! anonymous recursive function possible.
8//!
9//! ```
10//! use recur_fn::{recur_fn, RecurFn};
11//!
12//! let fib = recur_fn(|fib, n: i32| {
13//! if n <= 1 {
14//! n
15//! } else {
16//! fib(n - 1) + fib(n - 2)
17//! }
18//! });
19//!
20//! assert_eq!(55, fib.call(10));
21//! ```
22//!
23//! Beside, it makes extending the body of a recursive function possible.
24//!
25//! ```
26//! use recur_fn::{recur_fn, RecurFn};
27//! use std::cell::RefCell;
28//!
29//! let fib = recur_fn(|fib, n: i32| {
30//! if n <= 1 {
31//! n
32//! } else {
33//! fib(n - 1) + fib(n - 2)
34//! }
35//! });
36//!
37//! let log = RefCell::new(Vec::new());
38//!
39//! let fib_with_logging = recur_fn(|recur, n: i32| {
40//! log.borrow_mut().push(n);
41//! fib.body(recur, n)
42//! });
43//!
44//! fib_with_logging.call(3);
45//! assert_eq!(*log.borrow(), vec![3, 2, 1, 0, 1]);
46//! ```
47//!
48//! As `recur_fn` is a convenient way to construct a `RecurFn`,
49//! calling it is slower than direct recursion.
50//! To make it zero-cost, consider defining a struct,
51//! implementing `RecurFn` trait for it and mark the `body` method by `#[inline]`.
52//!
53//! ```
54//! use recur_fn::RecurFn;
55//!
56//! let fib = {
57//! struct Fib {}
58//! impl RecurFn<i32, i32> for Fib {
59//! #[inline]
60//! fn body(&self, fib: impl Fn(i32) -> i32, n: i32) -> i32 {
61//! if n <= 1 {
62//! n
63//! } else {
64//! fib(n - 1) + fib(n - 2)
65//! }
66//! }
67//! }
68//! Fib {}
69//! };
70//!
71//! assert_eq!(55, fib.call(10));
72//! ```
73//!
74//! or if the function doesn't capture anything,
75//! you can use `recur_fn` macro.
76//!
77//! ```
78//! use recur_fn::{recur_fn, RecurFn};
79//!
80//! let fact = recur_fn!(fact(n: i32) -> i32 {
81//! if n == 0 { 1 } else { n * fact(n - 1) }
82//! });
83//! assert_eq!(6, fact.call(3));
84//! assert_eq!(0,
85//! fact.body(|_| 0, 3));
86//! ```
87//!
88//! `DynRecurFn` is a dynamic version of `RecurFn`
89//! that allows you to have a trait object.
90//!
91//! ```
92//! use recur_fn::{recur_fn, RecurFn, DynRecurFn};
93//! use core::ops::Deref;
94//!
95//! let dyn_fact: &dyn DynRecurFn<_, _> =
96//! &recur_fn(|fact, n: i32| if n == 0 { 1 } else { n * fact(n - 1) });
97//! ```
98
99#![no_std]
100use core::ops::Deref;
101
102/// The recursive function trait.
103///
104/// Instead of recurring directly,
105/// this trait allows user to customize the recursion.
106/// In this way, we can extract and extend the body of a recursive function.
107///
108/// This trait supports only one argument.
109/// If you need multiple arguments, use tuple.
110pub trait RecurFn<Arg, Output> {
111 /// The body of the recursive function.
112 ///
113 /// Marking this method by `#[inline]` will make the `call` method
114 /// as fast as recurring directly.
115 fn body(&self, recur: impl Fn(Arg) -> Output, arg: Arg) -> Output;
116
117 /// Calls the recursive function.
118 #[inline]
119 fn call(&self, arg: Arg) -> Output {
120 self.body(|arg| self.call(arg), arg)
121 }
122}
123
124/// The dynamic version of `RecurFn` that supports trait object.
125pub trait DynRecurFn<Arg, Output> {
126 /// The body of the recursive function.
127 fn dyn_body(&self, recur: &dyn Fn(Arg) -> Output, arg: Arg) -> Output;
128}
129
130impl<Arg, Output, R> DynRecurFn<Arg, Output> for R
131where
132 R: RecurFn<Arg, Output>,
133{
134 fn dyn_body(&self, recur: &dyn Fn(Arg) -> Output, arg: Arg) -> Output {
135 self.body(recur, arg)
136 }
137}
138
139macro_rules! impl_dyn_with_markers {
140 ($($marker:ident),*) => {
141 impl<'a, Arg, Output> RecurFn<Arg, Output> for dyn DynRecurFn<Arg, Output> + 'a$( + $marker)*
142 {
143 fn body(&self, recur: impl Fn(Arg) -> Output, arg: Arg) -> Output {
144 self.dyn_body(&recur, arg)
145 }
146 }
147 };
148}
149impl_dyn_with_markers! {}
150impl_dyn_with_markers! {Send}
151impl_dyn_with_markers! {Sync}
152impl_dyn_with_markers! {Send, Sync}
153
154/// A `RecurFn` that delegates to a pointer to a `RecurFn`.
155pub struct FromPointer<D>(D);
156impl<Arg, Output, D> RecurFn<Arg, Output> for FromPointer<D>
157where
158 D: Deref,
159 D::Target: RecurFn<Arg, Output>,
160{
161 #[inline]
162 fn body(&self, recur: impl Fn(Arg) -> Output, arg: Arg) -> Output {
163 self.0.body(recur, arg)
164 }
165}
166
167/// Returns a `RecurFn` implementation from a pointer
168/// to `RecurFn`, i.e. a implementation of `Deref` whose `Target`
169/// implements `RecurFn`.
170///
171/// # Examples
172///
173/// ```
174/// use recur_fn::{RecurFn, recur_fn, from_pointer};
175///
176/// fn test_fact(fact: impl RecurFn<i32, i32>) {
177/// assert_eq!(fact.call(5), 120);
178/// }
179/// let box_fact = Box::new(recur_fn(
180/// |fact, n: i32| {
181/// if n <= 1 {
182/// 1
183/// } else {
184/// n * fact(n - 1)
185/// }
186/// },
187/// ));
188/// test_fact(from_pointer(box_fact));
189/// ```
190pub fn from_pointer<Arg, Output, D>(d: D) -> FromPointer<D>
191where
192 D: Deref,
193 D::Target: RecurFn<Arg, Output>,
194{
195 FromPointer(d)
196}
197
198/// A `RecurFn` that doesn't call `recur` parameter in its body.
199pub struct Direct<F>(F);
200
201impl<Arg, Output, F> RecurFn<Arg, Output> for Direct<F>
202where
203 F: Fn(Arg) -> Output,
204{
205 #[inline]
206 fn body(&self, _: impl Fn(Arg) -> Output, arg: Arg) -> Output {
207 (self.0)(arg)
208 }
209
210 fn call(&self, arg: Arg) -> Output {
211 (self.0)(arg)
212 }
213}
214
215/// Constructs a non-recursive `RecurFn` calling `f` directly.
216///
217/// # Examples
218///
219/// ```
220/// use recur_fn::{RecurFn, direct};
221///
222/// let double = direct(|n: i32| n * 2);
223/// assert_eq!(4, double.call(2));
224/// assert_eq!(20, double.body(|_| 0, 10));
225/// ```
226pub fn direct<Arg, Output, F: Fn(Arg) -> Output>(f: F) -> Direct<F> {
227 Direct(f)
228}
229
230/// A `RecurFn` that uses a closure as the body.
231pub struct Closure<F>(F);
232
233impl<Arg, Output, F> RecurFn<Arg, Output> for Closure<F>
234where
235 F: Fn(&dyn Fn(Arg) -> Output, Arg) -> Output,
236{
237 fn body(&self, recur: impl Fn(Arg) -> Output, arg: Arg) -> Output {
238 self.0(&recur, arg)
239 }
240
241 fn call(&self, arg: Arg) -> Output {
242 self.0(&|arg| self.call(arg), arg)
243 }
244}
245
246/// Constructs a `RecurFn` with the body speicified.
247///
248/// # Examples
249///
250/// ```
251/// use recur_fn::{recur_fn, RecurFn};
252///
253/// let fib = recur_fn(|fib, n: i32| {
254/// if n <= 1 {
255/// n
256/// } else {
257/// fib(n - 1) + fib(n - 2)
258/// }
259/// });
260///
261/// assert_eq!(55, fib.call(10));
262/// ```
263pub fn recur_fn<Arg, Output, F>(body: F) -> Closure<F>
264where
265 F: Fn(&dyn Fn(Arg) -> Output, Arg) -> Output,
266{
267 Closure(body)
268}
269
270/// Expands a function definition to defining a struct,
271/// implementing `RecurFn` for the struct and constructing it.
272/// It can be useful if you want a zero-cost `RecurFn` implementation.
273///
274/// The function should have exactly one argument.
275/// `impl Trait`s and generics are not supported.
276///
277/// # Examples
278///
279/// ```
280/// use recur_fn::{recur_fn, RecurFn};
281///
282/// let fact = recur_fn!(fact(n: i32) -> i32 {
283/// if n == 0 { 1 } else { n * fact(n - 1) }
284/// });
285/// assert_eq!(6, fact.call(3));
286/// assert_eq!(0,
287/// fact.body(|_| 0, 3));
288/// ```
289#[macro_export]
290macro_rules! recur_fn {
291 ($fn_name:ident ($arg_name:ident: $arg_type:ty) -> $output_type:ty $body:block) => {{
292 #[allow(non_camel_case_types)]
293 struct $fn_name {}
294
295 impl RecurFn<$arg_type, $output_type> for $fn_name {
296 #[inline]
297 fn body(
298 &self,
299 $fn_name: impl Fn($arg_type) -> $output_type,
300 $arg_name: $arg_type,
301 ) -> $output_type {
302 $body
303 }
304 }
305 $fn_name {}
306 }};
307}
308
309#[cfg(test)]
310mod tests;