Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to implement Landin's Knot in Rust with closures?

Landin’s knot is a trick for implementing recursion without the need to be able to reference a function’s own name (useful if a language does not natively support recursion).

In pseudo-code, we can create a function "f" that recursively calls itself as follows:

let id = (x) => x    // create a temporary function
let r = id           // store the function in a reference
let f = (n) => !r(n) // create f, a function that unwraps the reference and calls it
r = f                // reassign r to f
f(0)                 // f achieves recursion

The above example would loop forever / cause a stack overflow, but the definition of f could be trivially adjusted to include base cases or calculate fibonacci instead etc.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

I tried the naive approach below, but I could not get it working due to compilation errors:

trait I32Fn {
    fn call(&mut self, x: i32) -> i32;
}

struct Closure {
    f: Box<dyn FnMut(i32) -> i32>,
}

impl I32Fn for Closure {
    fn call(&mut self, x: i32) -> i32 {
        (self.f)(x)
    }
}

fn main() {
    let id: Box<dyn I32Fn> = Box::new(Closure { f: Box::new(|x| x) });
    let mut r = id;
    let mut f: Box<dyn I32Fn> = Box::new(Closure {
        f: Box::new(|n: i32| -> i32 { (*r).call(n) }),
    });
    *r = *f;
    println!("{}", (*f).call(0));
}

Presumably if we store closures on the heap in some orderly way (in an arena perhaps) then there should be some way to safely allow a closure to refer to itself?

>Solution :

This step:

r = f                // reassign r to f

Is in fundamental contradiction with Rust’s rules: f holds a reference to r yet we assign it (we broke aliasable xor mutable).

But there is a solution to that: interior mutability.

Also this:

f(0)                 // f achieves recursion

Is quite a problem since we moved f in the previous step. But again, there are solutions to that: for example, reference counting.

All in all:

use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let id: Rc<RefCell<Rc<dyn Fn(i32) -> i32>>> = Rc::new(RefCell::new(Rc::new(|x| x)));
    let mut r = id;
    let f: Rc<dyn Fn(i32) -> i32> = Rc::new({
        let r = Rc::clone(&r);
        move |n| {
            dbg!(n);
            r.borrow()(n + 1)
        }
    });
    *r.borrow_mut() = Rc::clone(&f);
    f(0);
}

Playground.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading