How to alias an impl trait? - rust

I have many functions of the following type signature:
fn f() -> impl Fn(u32) -> u32 {
|x: u32| x
}
How can I give a name to Fn(u32) -> u32 so that I don't have to repeat it? Although I can do type X = Fn(u32) -> u32;, Rust will not let me use this because it is a type and not a trait. Must I wait for trait_alias or can I do something else?

You're exactly right. impl X requires X to be a trait, and it's impossible to have proper trait aliases until trait aliases land. When that happens you'll be able to do this:
#![feature(trait_alias)]
trait X = Fn(u32) -> u32;
fn f() -> impl X {
|x: u32| x
}
(playground)
Alternatively, when Permit impl Trait in type aliases lands, you'll be able to make impl trait a type alias. This is slightly different though. When we alias with type X = impl Trait, the compiler will ensure that every usage of X is actually the same concrete type. That would mean that, in your case, you wouldn't be able to use this with multiple different closures, since every closure has its own unique type.
#![feature(type_alias_impl_trait)]
type X = impl Fn(u32) -> u32;
fn f() -> X {
|x: u32| x
}
(playground)
However, this won't compile.
#![feature(type_alias_impl_trait)]
type X = impl Fn(u32) -> u32;
fn f() -> X {
|x: u32| x
}
// Even a closure with exactly the same form has a different type.
fn g() -> X {
|x: u32| x
}
The error is
error: concrete type differs from previous defining opaque type use
--> src/lib.rs:10:1
|
10 | / fn g() -> X {
11 | | |x: u32| x
12 | | }
| |_^ expected `[closure#src/lib.rs:7:5: 7:15]`, got `[closure#src/lib.rs:11:5: 11:15]`
|
note: previous use here
--> src/lib.rs:6:1
|
6 | / fn f() -> X {
7 | | |x: u32| x
8 | | }
| |_^
(playground)
This is in contrast to trait aliases, which would allow a different concrete type to be used with every function returning impl TraitAlias. See the RFCs that introduced this syntax and existential types in general for more.
Until one of those two features lands, you can get similar behavior to the trait alias with what is essentially a hack. The idea is to make a new trait which is essentially equivalent to the original trait, but has a shorter name.
// This trait is local to this crate,
// so we can implement it on any type we want.
trait ShortName: Fn(u32) -> u32 {}
// So let's go ahead and implement `ShortName`
// on any type that implements `Fn(u32) -> u32`.
impl<T: Fn(u32) -> u32> ShortName for T {}
// We can use `ShortName` to alias `Fn(u32) -> u32`.
fn f() -> impl ShortName {
|x: u32| x
}
// Moreover, the result of that can be used in places
// that expect `Fn(u32) -> u32`.
fn g<T: Fn(u32) -> u32>(x: &T) -> u32 {
x(6_u32)
}
fn main() {
// We only know that `x` implements `ShortName`,
let x = f();
// But we can still use `x` in `g`,
// which expects an `Fn(u32) -> u32` argument
let _ = g(&x);
}
(playground)

Related

How to implement flatten(ffa: F<F<A>>) -> F<A> in Rust?

question and my goal
My goal is to let this (playground) compile. Below is the core of the code. This is attempt to implement FlatMap for all F<F<A>>, such as Option<Option<i32>>.
trait HKT1 {
type Unwrapped;
type Wrapped<T>;
}
trait FlatMap: HKT1 + Sized {
fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
// Below cannot compile
fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>
// How to set generic bound correctly?
where
Self::Wrapped<Self::Wrapped<A>>: FlatMap
{
ffa.flat_map(|x| x)
}
}
error[E0308]: mismatched types
--> src/main.rs:15:26
|
15 | ffa.flat_map(|x| x)
| ^ expected HKT1::Wrapped, found HKT1::Unwrapped
|
= note: expected associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Wrapped<_>`
found associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Unwrapped`
error[E0308]: mismatched types
--> src/main.rs:15:9
|
6 | trait FlatMap: HKT1 + Sized {
| --------------------------- this type parameter
...
11 | fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>
| ---------------- expected `<Self as HKT1>::Wrapped<A>` because of return type
...
15 | ffa.flat_map(|x| x)
| ^^^^^^^^^^^^^^^^^^^ expected type parameter `Self`, found associated type
|
= note: expected associated type `<Self as HKT1>::Wrapped<A>`
found associated type `<<Self as HKT1>::Wrapped<<Self as HKT1>::Wrapped<A>> as HKT1>::Wrapped<_>`
= note: you might be missing a type parameter or trait bound
HKT1 is a way to emulate F<_> and allow all traits that are based on it to access F and _ separately. I learned from this blog.
what I am trying to do
I'm trying to build up a functional programming crate which is kind of translating Scala lib cats to Rust. (Current work here). To implement the higher kinded type, I follow this blog and define the HKT1 trait. It is quite elegant until I want to implement flatten(ffa: F<F<A>>) -> F<A> but cannot figure out how to set the generic bound correctly.
It seems easy to implement if I leave it blank in the trait. For example:
trait FlatMap: HKT1 + Sized {
fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
fn flatten<A>(ffa: Self::Wrapped<Self::Wrapped<A>>) -> Self::Wrapped<A>;
}
struct MyF<T>(pub T);
impl<A> HKT1 for MyF<A> {
type Unwrapped = A;
type Wrapped<T> = MyF<T>;
}
impl<T> FlatMap for MyF<T> {
fn flat_map<B, F>(self, f: F) -> MyF<B>
where
F: FnOnce(Self::Unwrapped) -> MyF<B>,
{
f(self.0)
}
fn flatten<A>(ffa: MyF<MyF<A>>) -> MyF<A> {
ffa.flat_map(|x| x)
}
}
But I would like to have a default implementation through flat_map. How can I achieve this?
A solution via EqT
I just learned how to achieve this from the implementation of crate functional. (Well, the implementation is a little different from above)
The key idea is to add a trait EqT, whose purpose is to assert two types are equal. (playground)
trait HKT1 {
type Unwrapped;
type Wrapped<T>;
}
trait FlatMap: HKT1 + Sized {
fn flat_map<B, F>(self, f: F) -> Self::Wrapped<B>
where
F: FnOnce(Self::Unwrapped) -> Self::Wrapped<B>;
fn flatten<A>(self) -> Self::Wrapped<A>
where
Self::Unwrapped: EqT<Self::Wrapped<A>>,
{
self.flat_map(|x| x.cast())
}
}
trait EqT<T> {
fn cast(self) -> T;
}
impl<T> EqT<T> for T {
fn cast(self) -> T {
self
}
}
Then we can easily implement and use flatten:
struct MyF<T>(pub T);
impl<A> HKT1 for MyF<A> {
type Unwrapped = A;
type Wrapped<T> = MyF<T>;
}
impl<T> FlatMap for MyF<T> {
fn flat_map<B, F>(self, f: F) -> MyF<B>
where
F: FnOnce(Self::Unwrapped) -> MyF<B>,
{
f(self.0)
}
}
let ffa = MyF(MyF(1));
let fa = ffa.flatten();
println!("{:?}", fa); // Expect MyF(1)

Covariance of Box<dyn FnOnce(T)> in rust

I have a function that expects a short lived object. I would expect that I would be able to always pass it a long lived object. But I am getting a strange error when I try to encode that:
type F<'arg> = Box<dyn FnOnce(&'arg ())>;
fn contravar<'small, 'large: 'small>(f: F<'small>) -> F<'large> {
f
}
playground
Particularly:
error: lifetime may not live long enough
--> src/lib.rs:3:5
|
2 | fn contravar<'small, 'large: 'small>(f: F<'small>) -> F<'large> {
| ------ ------ lifetime `'large` defined here
| |
| lifetime `'small` defined here
3 | f
| ^ function was supposed to return data with lifetime `'large` but it is returning data with lifetime `'small`
|
= help: consider adding the following bound: `'small: 'large`
It seems like F is invariant for its argument but I would have guessed that it's contravariant. Am I missing something? Is there a way to make F<'arg> really contravariant for 'arg?
Edit: it looks like the "problem" is that rust wants to treat all generic traits the same (including Fn/FnMut/FnOnce). My opinion is that those 3 are and should be treated special especially given that they are the only way to refer to closures. For that reason I opened an issue
The Rust Reference's page on Subtyping and Variance documents that, as of Rust 1.63.0, fn(T) -> () is contravariant over T and that dyn Trait<T> + 'a is invariant over T.
FnOnce, FnMut and Fn are traits, so that means dyn FnOnce(&'a ()) is unfortunately invariant over &'a ().
// Compiles
pub fn contravariant<'a, 'b: 'a>(x: fn(&'a ())) -> fn(&'b ()) { x }
// Doesn't compile
pub fn contravariant2<'a, 'b: 'a>(x: Box<dyn FnOnce(&'a ())>) -> Box<dyn FnOnce(&'b ())> { x }
Is there a way to wrap FnOnce somehow to convince the compiler of the correct variance?
Here's what I could come up with using unsafe code. Note that I'm not making any guarantees as to whether this is sound or not. I don't know of any way to do this without unsafe code.
use std::marker::PhantomData;
trait Erased {}
impl<T> Erased for T {}
pub struct VariantBoxedFnOnce<Arg, Output> {
boxed_real_fn: Box<dyn Erased + 'static>,
_phantom_fn: PhantomData<fn(Arg) -> Output>,
}
impl<Arg, Output> VariantBoxedFnOnce<Arg, Output> {
pub fn new(real_fn: Box<dyn FnOnce(Arg) -> Output>) -> Self {
let boxed_real_fn: Box<dyn Erased + '_> = Box::new(real_fn);
let boxed_real_fn: Box<dyn Erased + 'static> = unsafe {
// Step through *const T because *mut T is invariant over T
Box::from_raw(Box::into_raw(boxed_real_fn) as *const (dyn Erased + '_) as *mut (dyn Erased + 'static))
};
Self {
boxed_real_fn,
_phantom_fn: PhantomData,
}
}
pub fn call_once(self, arg: Arg) -> Output {
let boxed_real_fn: Box<Box<dyn FnOnce(Arg) -> Output>> = unsafe {
// Based on Box<dyn Any>::downcast()
Box::from_raw(Box::into_raw(self.boxed_real_fn) as *mut Box<dyn FnOnce(Arg) -> Output>)
};
boxed_real_fn(arg)
}
}
pub fn contravariant<'a, 'b: 'a>(x: VariantBoxedFnOnce<&'a (), ()>) -> VariantBoxedFnOnce<&'b (), ()> { x }
#[cfg(test)]
mod tests {
use super::*;
fn foo(_x: &()) {}
#[test]
pub fn check_fn_does_not_require_static() {
let f = VariantBoxedFnOnce::new(Box::new(foo));
let x = ();
f.call_once(&x);
}
#[test]
pub fn check_fn_arg_is_contravariant() {
let f = VariantBoxedFnOnce::new(Box::new(foo));
let g = contravariant(f);
let x = ();
g.call_once(&x);
}
}
Here, VariantBoxedFnOnce is limited to functions taking one argument.
The trick is to store a type-erased version of the Box<dyn FnOnce(Arg) -> Output> such that Arg disappears, because we don't want the variance of VariantBoxedFnOnce<Arg, Output> to depend on Box<dyn FnOnce(Arg) -> Output> (which is invariant over Arg). However, there's also a PhantomData<fn(Arg) -> Output> field to provide the proper contravariance over Arg (and covariance over Output).
We can't use Any as our erased type, because only 'static types implement Any, and we have a step in VariantBoxedFnOnce::new() where we have a Box<dyn Erased + '_> where '_ is not guaranteed to be 'static. We then immediately "transmute" it into 'static, to avoid having a redundant lifetime parameter on VariantBoxedFnOnce, but that 'static is a lie (hence the unsafe code). call_once "downcasts" the erased type to the "original" Box<dyn FnOnce(Arg) -> Output>, except that Arg and Output may be different from the original due to variance.

How to implement a trait for a generic type of function pointer, no matter whose arguments types are references or not

I want: for a trait, e.g. Foo, any fn(T1,T2)->() should implement it, and I don't care whether T1 or T2 is a reference or not.
trait Foo {
fn hello_world(&self) -> String {
"hello world".into()
}
}
impl<T1, T2> Foo for fn(T1, T2) -> () {}
fn a_dummy_sample_function(_: i32, _:i32) {}
fn main() {
let fn_ptr : fn(i32, i32)->() = a_dummy_sample_function;
(fn_ptr).hello_world();
}
So far, it works well.
If I change the type of first argument to &i32,
trait Foo {
fn hello_world(&self) -> String {
"hello world".into()
}
}
impl<T1, T2> Foo for fn(T1, T2) -> () {}
fn foo_imp(_: &i32, _:i32) {}
fn main() {
let fn_ptr : fn(&i32, i32)->() = foo_imp;
fn_ptr.hello_world();
}
Then, I've got the following compilation errors.
--> src/main.rs:12:12
|
12 | fn_ptr.hello_world();
| ^^^^^^^^^^^ method not found in `for<'r> fn(&'r i32, i32)`
|
= note: `fn_ptr` is a function, perhaps you wish to call it
= help: items from traits can only be used if the trait is implemented and in scope
note: `Foo` defines an item `hello_world`, perhaps you need to implement it
I can fix the compilation error as below
impl<T1, T2> Foo for for<'a> fn(&'a T1, T2) -> () {}
But it is not what I want, I want any function pointer with any types of two arguments whether it is a reference or not.

Rust function which takes function with arg a function

I want to write a generic function count_calls which calls a function f which takes a function pointer (lambda) where count_calls counts how often function f called the given lambda function.
I struggle with the approach (Playground).
fn count_calls<S, F>(s: S, f: F) -> u32
where
S: Clone,
F: Sized + FnMut(Fn() -> S) -> (),
{
let mut counter: u32 = 0;
f(|| {
counter += 1;
s.clone()
});
counter
}
#[cfg(test)]
mod stackoverflow {
use super::*;
fn f(p: fn() -> i32) {
p();
p();
}
#[test]
fn test() {
let counts = count_calls(3, f);
assert_eq!(counts, 2);
}
}
Here I get the error:
error[E0277]: the size for values of type `(dyn std::ops::Fn() -> S + 'static)` cannot be known at compilation time
--> src/lib.rs:1:1
|
1 | / fn count_calls<S, F>(s: S, f: F) -> u32
2 | | where
3 | | S: Clone,
4 | | F: Sized + FnMut(Fn() -> S) -> (),
... |
12 | | counter
13 | | }
| |_^ doesn't have a size known at compile-time
|
= help: within `((dyn std::ops::Fn() -> S + 'static),)`, the trait `std::marker::Sized` is not implemented for `(dyn std::ops::Fn() -> S + 'static)`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: required because it appears within the type `((dyn std::ops::Fn() -> S + 'static),)`
= note: required by `std::ops::FnMut`
Does someone know how to fix this?
[Edit]
I think using Box<Fn()->S> might be a solution. But I would prefer a stack only solution, if possible.
The error "the size for values of type (dyn std::ops::Fn() -> S + 'static) cannot be known at compilation time" is caused by your trait bound for F:
F: Sized + FnMut(Fn() -> S) -> ()
This is equivalent to F: Sized + FnMut(dyn Fn() -> S). This means that the closure F would take a trait object (dyn Fn() -> S) by value. But trait objects are unsized and cannot be passed by value (yet).
One solution would be to pass the trait object by reference or in a Box. The answer by rodrigo explains and discusses these solutions.
Can we avoid trait objects and dynamic dispatch?
Not properly, I think.
Non solutions
One idea would be to add another type parameter to count_calls:
fn count_calls<S, F, G>(s: S, f: F) -> u32
where
S: Clone,
F: Sized + FnMut(G),
G: Fn() -> S,
However, this doesn't work:
error[E0308]: mismatched types
--> src/lib.rs:9:7
|
9 | f(|| {
| _______^
10 | | counter += 1;
11 | | s.clone()
12 | | });
| |_____^ expected type parameter, found closure
|
= note: expected type `G`
found type `[closure#src/lib.rs:9:7: 12:6 counter:_, s:_]`
The problem here is that type arguments of count_calls are chosen by the caller of count_calls. But we actually want G to always be the type of our own closure. So that doesn't work.
What we want is a generic closure (where we can choose it's type parameters). Something similar is possible, but restricted to lifetime parameters. It's called HRTBs and looks like F: for<'a> Fn(&'a u32). But it doesn't help here because we need a type parameter and for<T> doesn't exist (yet?).
Sub-optimal, nightly solution
One solution would be to not use a closure, but a type with a known name which implements FnMut. Sadly, you can't implement the Fn* traits for your own type on stable yet. On nightly, this works.
struct CallCounter<S> {
counter: u32,
s: S,
}
impl<S: Clone> FnOnce<()> for CallCounter<S> {
type Output = S;
extern "rust-call" fn call_once(self, _: ()) -> Self::Output {
// No point in incrementing the counter here
self.s
}
}
impl<S: Clone> FnMut<()> for CallCounter<S> {
extern "rust-call" fn call_mut(&mut self, _: ()) -> Self::Output {
self.counter += 1;
self.s.clone()
}
}
fn count_calls<S, F>(s: S, mut f: F) -> u32
where
S: Clone,
F: Sized + FnMut(&mut CallCounter<S>), // <----
{
let mut counter = CallCounter {
counter: 0,
s,
};
f(&mut counter); // <-------
counter.counter
}
Unfortunately, now you have this strange type in your public interface (which should be implementation detail).
Apart from that, I can't think of any real solution (only other super verbose solutions with plenty of disadvantages). The developments in the type system corner (in particular in regards to GATs and HKTs) could solve this properly in the future. However, I think there are still a few different features lacking; in particular, I don't think that GATs as proposed would already solve this.
So if this is a real life problem which needs to be solved right now, I would:
step back and rethink the problem in a bigger scope to maybe avoid this Rust limitation, or
just use dynamic dispatch.
This is the simplest code that I managed to get working (playground):
fn count_calls<S, F>(s: S, mut f: F) -> u32
where
S: Clone,
F: FnMut(&mut dyn FnMut() -> S) -> (),
{
let mut counter: u32 = 0;
f(&mut || {
counter += 1;
s.clone()
});
counter
}
#[cfg(test)]
mod stackoverflow {
use super::*;
fn f(p: &mut dyn FnMut() -> i32) {
p();
p();
}
#[test]
fn test() {
let counts = count_calls(3, f);
assert_eq!(counts, 2);
}
}
The key change is that the function argument for F is changed from Fn() -> S into &mut dyn FnMut() -> S. You need a reference because you are using dynamic dispatching. Also you need FnMut because you are capturing counter and changing it inside, and a Fn will not allow it.
Note that you cannot use Box<FnMut() -> S. It will not allow capturing a reference to counter because boxed functions must be 'static.
If you find that changing your Fn to FnMut is undesirable (because you are changing your public API) you could go back to F: FnMut(&mut dyn Fn() -> S) -> () by defining counter as a Cell<u32>:
fn count_calls<S, F>(s: S, mut f: F) -> u32
where
S: Clone,
F: FnMut(&dyn Fn() -> S) -> (),
{
let counter: Cell<u32> = Cell::new(0);
f(&|| {
counter.set(counter.get() + 1);
s.clone()
});
counter.into_inner()
}

How to implement the Y combinator in Rust? [duplicate]

I've just started Rust tutorial and ended with such code using recursion
extern crate rand;
use std::io;
use rand::Rng;
use std::cmp::Ordering;
use std::str::FromStr;
use std::fmt::{Display, Debug};
fn try_guess<T: Ord>(guess: T, actual: T) -> bool {
match guess.cmp(&actual) {
Ordering::Less => {
println!("Too small");
false
}
Ordering::Greater => {
println!("Too big");
false
}
Ordering::Equal => {
println!("You win!");
true
}
}
}
fn guess_loop<T: Ord + FromStr + Display + Copy>(actual: T)
where <T as FromStr>::Err: Debug
{
println!("PLease input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess_int: T = guess.trim()
.parse()
.expect("Should enter integer number");
println!("You guessed {} !", guess_int);
if !try_guess(guess_int, actual) {
guess_loop(actual)
}
}
fn main() {
println!("Guess the number!!!");
let secret_number = rand::thread_rng().gen_range(1, 51);
guess_loop(secret_number);
}
I was hoping to factor-out the recursion from the guess_loop function and introduced a fix point operator:
fn guess_loop<T: Ord + FromStr + Display + Copy>(actual: T, recur: fn(T) -> ()) -> ()
where <T as FromStr>::Err: Debug
{
println!("PLease input your guess.");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess_int: T = guess.trim()
.parse()
.expect("Should enter integer number");
println!("You guessed {} !", guess_int);
if !try_guess(guess_int, actual) {
recur(actual)
}
}
fn fix<T, R>(func: fn(T, fn(T) -> R) -> R) -> fn(T) -> R {
fn fixed(val: T) -> R {
func(val, fixed)
}
fixed
}
fn main() {
println!("Guess the number!!!");
let secret_number = rand::thread_rng().gen_range(1, 51);
fix(guess_loop)(secret_number);
}
but this led to numerous errors, such as
error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
--> src/main.rs:49:19
|
49 | fn fixed(val: T) -> R {
| ^ use of type variable from outer function
error[E0401]: can't use type parameters from outer function; try using a local type parameter instead
--> src/main.rs:49:25
|
49 | fn fixed(val: T) -> R {
| ^ use of type variable from outer function
error[E0434]: can't capture dynamic environment in a fn item; use the || { ... } closure form instead
--> src/main.rs:50:9
|
50 | func(val, fixed)
| ^^^^
My next attempt was changing guess_loop's definition to
fn guess_loop<T: Ord + FromStr + Display + Copy, F>(actual: T, recur: F) -> ()
where <T as FromStr>::Err: Debug,
F: Fn(T) -> ()
{ ... }
and redefine fix as
fn fix<T, R, F>(func: fn(T, F) -> R) -> F
where F: Fn(T) -> R
{
let fixed = |val: T| func(val, fix(func));
fixed
}
this led to
error[E0308]: mismatched types
--> src/main.rs:53:5
|
53 | fixed
| ^^^^^ expected type parameter, found closure
|
= note: expected type `F`
= note: found type `[closure#src/main.rs:52:17: 52:46 func:_]`
error: the type of this value must be known in this context
--> src/main.rs:61:5
|
61 | fix(guess_loop)(secret_number);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How can I write a similar fix function?
Firstly, variable names don't exist until after they're initialised. You can't have fixed refer to itself like that.
Secondly, you can't return closures by-value from a function, period. Generic parameters are chosen by the caller, and the caller has no idea what the type of a closure inside the function is going to be.
I'm not claiming that what follows is the best way of doing this, but it was the simplest I was able to come up with that type-checks.
fn guess_loop<T>(actual: T, recur: &Fn(T)) -> ()
where T: Ord + FromStr + Display + Copy,
<T as FromStr>::Err: Debug
{
// ...
}
fn fix<T, R, F>(func: F) -> Box<Fn(T) -> R>
where T: 'static,
R: 'static,
F: Fn(T, &Fn(T) -> R) -> R + 'static
{
use std::cell::RefCell;
use std::rc::Rc;
let fixed = Rc::new(RefCell::new(None));
let fixed_fn = {
let fixed = fixed.clone();
move |val: T| -> R {
let fixed_ref = fixed.borrow();
let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
func(val, &**fixed_ref)
}
};
*fixed.borrow_mut() = Some(Box::new(fixed_fn));
Box::new(move |val: T| -> R {
let fixed_ref = fixed.borrow();
let fixed_ref: &Box<_> = fixed_ref.as_ref().unwrap();
fixed_ref(val)
})
}
In order for fixed_fn to refer to itself, we have to create something for it to read from before it exists. Unfortunately, this means having a cycle, and Rust hates cycles. So, we do this by constructing a reference-counted RefCell<Option<_>> that starts with None, and which will be mutated later to contain the fixed-point closure.
Secondly, we can't use this handle as a callable, so we have to explicitly pull a pointer to the closure out so that we can pass it to func.
Third, the compiler doesn't seem to be able to infer the type of fixed correctly. I was hoping it would be able to work out that it is Rc<RefCell<Option<{closure}>>>, but it refused to do so. As a result, we have to resort to storing a Box<Fn(T) -> R>, since we can't name the type of the closure explicitly.
Finally, we have to construct a new closure that takes a second handle to fixed, unpacks it, and calls it. Again, we can't use fixed as a callable directly. We also can't re-use the closure inside fixed, because to do that we'd have to put that inside its own Rc and at that point, things are starting to get crazy.
... more crazy.
Finally, we have to return this second closure in a Box because, as I said before, we can't return closures by value because we can't name their types in the signature.
*deep breath*
If someone has a simpler solution, I'd love to see it. :P
This is an answer to my own question about implementing the Y combinator which is a subset of this question. In pure lambda expression, a version of the Y combinator looks like
λf.(λw.w w)(λw.f (w w))
The solution in Rosetta Code is too complicated and used Box to allocate memory in the heap. I want to simplify this.
First, let's implement the type Mu<T> as a trait instead.
trait Mu<T> {
fn unroll(&self, &Mu<T>) -> T;
}
Note that we need this trait to be object safe, which means we cannot ask for Self in any of its definition so the second parameter is typed &Mu<T> and it is a trait object.
Now we can write a generic trait implementation:
impl<T, F: Fn(&Mu<T>) -> T> Mu<T> for F {
fn unroll(&self, o: &Mu<T>) -> T {
self(o)
}
}
With this, we can now write the y combinator as the following:
fn y<T, F: Fn(T) -> T>(f: &F) -> T {
(&|w: &Mu<T>| w.unroll(w))(&|w: &Mu<T>| f(w.unroll(w)))
}
The above compiles in the Rust playground without enabling any features and using only the stable channel so this is a pretty good answer to my question.
However, the above would not work in practice because Rust is call-by-value but the code above is the call-by-name Y combinator.
The call-by-value solution
To work with the stable channel without requiring any features, we cannot return closures (which requires impl Trait). Instead, I came up with making another Mu2 type that takes two type parameters:
trait Mu2<T, R> {
fn unroll(&self, &Mu2<T, R>, t: T) -> R;
}
As above, let's implement this new trait.
impl<T, R, F> Mu2<T, R> for F
where
F: Fn(&Mu2<T, R>, T) -> R,
{
fn unroll(&self, o: &Mu2<T, R>, t: T) -> R {
self(o, t)
}
}
The new Y combinator:
fn y<T, R, F>(f: &F, t: T) -> R
where
F: Fn(&Fn(T) -> R, T) -> R,
{
(&|w: &Mu2<T, R>, t| w.unroll(w, t))((&|w: &Mu2<T, R>, t| f(&|t| w.unroll(w, t), t)), t)
}
Now it is time to test our new facility.
fn main() {
let fac = &|f: &Fn(i32) -> i32, i| if i > 0 { i * f(i - 1) } else { 1 };
println!("{}", y(fac, 10))
}
Results in:
3628800
All done!
You can see that the y function has a slightly different signature than the questioner's fix, but it shouldn't matter.
The direct recurring version
The same technology to avoid returning a closure can be used for the normal direct recurring version as well:
fn fix<T, R, F>(f: &F, t: T) -> R
where
F: Fn(&Fn(T) -> R, T) -> R,
{
f(&|t| fix(f, t), t)
}
fn fib(i: i32) -> i32 {
let fn_ = &|f:&Fn(i32) -> i32, x| if x < 2 { x } else { f(x-1) + f(x-2) };
fix(fn_, i)
}
Basically, whenever you need to return a closure from a function, you can add the closure's parameter to the function, and change the return type to the closure's return type. Later on when you need a real closure, just create the closure by partial evaluating that function.
Further discussions
Compare to other languages, in Rust there is a big difference: the function given to find fix point must not have any internal states. In Rust this is a requirement that the F type parameter of y must be Fn, not FnMut or FnOnce.
For example, we cannot implement a fix_mut that would be used like
fn fib1(i: u32) -> u32 {
let mut i0 = 1;
let mut i1 = 1;
let fn_ = &mut |f:&Fn(u32) -> u32, x|
match x {
0 => i0,
1 => i1,
_ => {
let i2 = i0;
i0 = i1;
i1 = i1 + i2;
f(x)
}
};
fix_mut(fn_, i)
}
without unsafe code whilst this version, if it works, performs much better (O(N)) than the version given above (O(2^N)).
This is because you can only have one &mut of one object at a single time. But the idea of Y combinator, or even the fix point function, requires capturing/passing the function at the same time when calling it, that's two references and you can't just mark any of them immutable without marking another so.
On the other hand, I was wonder if we could do something that other languages usually not able to but Rust seems to be able. I was thinking restricting the first argument type of F from Fn to FnOnce (as y function will provide the implementation, change to FnMut does not make sense, we know it will not have states, but change to FnOnce means we want it to be used only once), Rust would not allow at the moment as we cannot pass unsized object by value.
So basically, this implementation is the most flexible solution we could think of.
By the way, the work around of the immutable restriction is to use pseudo-mutation:
fn fib(i: u32) -> u32 {
let fn_ = &|f:&Fn((u32,u32,u32)) -> u32, (x,i,j)|
match x {
0 => i,
1 => j,
_ => {
f((x-1,j,i+j))
}
};
fix(&fn_, (i,1,1))
}
Starting at where you left off:
fn fix<T, R, F>(func: fn(T, F) -> R) -> F
where F: Fn(T) -> R
{
|val: T| func(val, fix(func))
}
The returned object has an unnameable closure type. Using a generic type won’t help here, since the type of the closure is decided by the callee, not the caller. Here’s where impl traits come in handy:
fn fix<T, R, F>(func: fn(T, F) -> R) -> impl Fn(T) -> R
where F: Fn(T) -> R
{
|val: T| func(val, fix(func))
}
We can’t pass fix(func) to func because it expects a nameable type for F. We’ll have to settle for a trait object instead:
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> impl Fn(T) -> R {
|val: T| func(val, &fix(func))
}
Now it’s time to fight the lifetime checker. The compiler complains:
only named lifetimes are allowed in `impl Trait`, but `` was found in the type `…`
This is a somewhat cryptic message. Since impl traits are always 'static by default, this is a roundabout way of saying: “the closure does not live long enough for 'static”. To get the real error message, we append + 'static to the impl Fn(T) -> R and recompile:
closure may outlive the current function, but it borrows `func`, which is owned by the current function
So that was the real problem. It is borrowing func. We don’t need to borrow func because fn is Copy, so we can duplicate it as much as we want. Let’s prepend the closure with move and get rid of the + 'static from earlier:
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> impl Fn(T) -> R {
move |val: T| func(val, &fix(func))
}
And voila, it works! Well, almost … you’ll have to edit guess_loop and change fn(T) -> () to &Fn(T) -> (). I’m actually quite amazed that this solution doesn’t require any allocations.
If you can’t use impl traits, you can instead write:
fn fix<T, R>(func: fn(T, &Fn(T) -> R) -> R) -> Box<Fn(T) -> R>
where T: 'static,
R: 'static
{
Box::new(move |val: T| func(val, fix(func).as_ref()))
}
which is unfortunately not allocation-free.
Also, we can generalize the result a bit to allow arbitrary closures and lifetimes:
fn fix<'a, T, R, F>(func: F) -> impl 'a + Fn(T) -> R
where F: 'a + Fn(T, &Fn(T) -> R) -> R + Copy
{
move |val: T| func(val, &fix(func))
}
In the process of figuring out a solution for your problem, I ended up writing a simpler version of fix, which actually ended up guide me towards a solution to your fix function:
type Lazy<'a, T> = Box<FnBox() -> T + 'a>;
// fix: (Lazy<T> -> T) -> T
fn fix<'a, T, F>(f: F) -> T
where F: Fn(Lazy<'a, T>) -> T + Copy + 'a
{
f(Box::new(move || fix(f)))
}
Here’s a demonstration of how this fix function could be used to calculate the factorial:
fn factorial(n: u64) -> u64 {
// f: Lazy<u64 -> u64> -> u64 -> u64
fn f(fac: Lazy<'static, Box<FnBox(u64) -> u64>>) -> Box<FnBox(u64) -> u64> {
Box::new(move |n| {
if n == 0 {
1
} else {
n * fac()(n - 1)
}
})
}
fix(f)(n)
}
This can be done at zero runtime cost if you're willing to use unstable features (i.e. a nightly compiler) and willing to... obfuscate your code slightly.
First, we need to turn the result of fix into a named struct. This struct needs to implement Fn, so we'll implement it manually (this is an unstable feature).
#![feature(fn_traits)]
#![feature(unboxed_closures)]
extern crate rand;
use rand::Rng;
use std::cmp::Ordering;
fn try_guess<T: Ord>(guess: T, actual: T) -> bool {
match guess.cmp(&actual) {
Ordering::Less => {
println!("Too small");
false
}
Ordering::Greater => {
println!("Too big");
false
}
Ordering::Equal => {
println!("You win!");
true
}
}
}
struct Fix<F>
where F: Fn(i32, &Fix<F>)
{
func: F,
}
impl<F> FnOnce<(i32,)> for Fix<F>
where F: Fn(i32, &Fix<F>)
{
type Output = ();
extern "rust-call" fn call_once(self, args: (i32,)) -> Self::Output {
self.call(args)
}
}
impl<F> FnMut<(i32,)> for Fix<F>
where F: Fn(i32, &Fix<F>)
{
extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> Self::Output {
self.call(args)
}
}
impl<F> Fn<(i32,)> for Fix<F>
where F: Fn(i32, &Fix<F>)
{
extern "rust-call" fn call(&self, (val,): (i32,)) -> Self::Output {
(self.func)(val, self);
}
}
fn fix<F>(func: F) -> Fix<F>
where F: Fn(i32, &Fix<F>)
{
Fix { func: func }
}
fn guess_loop<F>(actual: i32, recur: &F)
where F: Fn(i32)
{
let guess_int = rand::thread_rng().gen_range(1, 51);
if guess_int != actual {
recur(actual)
}
}
fn main() {
let secret_number = rand::thread_rng().gen_range(1, 51);
fix(guess_loop)(secret_number);
}
However, we're not done yet. This fails to compile with the following error:
error[E0281]: type mismatch: the type `fn(i32, &_) {guess_loop::<_>}` implements the trait `for<'r> std::ops::Fn<(i32, &'r _)>`, but the trait `for<'r> std::ops::Fn<(i32, &'r Fix<fn(i32, &_) {guess_loop::<_>}>)>` is required (cyclic type of infinite size)
--> src/main.rs:77:5
|
77 | fix(guess_loop)(secret_number);
| ^^^
|
= note: required by `fix`
Note: In case you're not aware, in Rust, each function has its own, zero-sized type. If a function is generic, then each instantiation of that function will have its own type as well. For example, the type of guess_loop::<X> will be reported by the compiler as fn(i32, &X) {guess_loop::<X>} (as you can see in the error message above, except with underscores where the concrete type hasn't been resolved yet). That type can be coerced to a function pointer type implicitly in some contexts or explicitly with a cast (as).
The problem is that, in the expression fix(guess_loop), the compiler needs to instantiate guess_loop, which is a generic function, and it looks like the compiler isn't able to figure out the proper type to instantiate it with. In fact, the type we would like to set for type parameter F references the type of guess_loop. If we were to write it out in the style reported by the compiler, the type would look like fn(i32, &Fix<X>) {guess_loop::<Fix<&X>>}, where X is replaced by the type itself (you can see now where the "cyclic type of infinite size" comes from).
We can solve this by replacing the guess_loop function by a non-generic struct (we'll call it GuessLoop) that implements Fn by referring to itself. (You can't do this with a normal function because you can't name a function's type.)
struct GuessLoop;
impl<'a> FnOnce<(i32, &'a Fix<GuessLoop>)> for GuessLoop {
type Output = ();
extern "rust-call" fn call_once(self, args: (i32, &Fix<GuessLoop>)) -> Self::Output {
self.call(args)
}
}
impl<'a> FnMut<(i32, &'a Fix<GuessLoop>)> for GuessLoop {
extern "rust-call" fn call_mut(&mut self, args: (i32, &Fix<GuessLoop>)) -> Self::Output {
self.call(args)
}
}
impl<'a> Fn<(i32, &'a Fix<GuessLoop>)> for GuessLoop {
extern "rust-call" fn call(&self, (actual, recur): (i32, &Fix<GuessLoop>)) -> Self::Output {
let guess_int = rand::thread_rng().gen_range(1, 51);
if !try_guess(guess_int, actual) {
recur(actual)
}
}
}
fn main() {
let secret_number = rand::thread_rng().gen_range(1, 51);
fix(GuessLoop)(secret_number);
}
Notice that GuessLoop's implementation of Fn is no longer generic on the type of the recur parameter. What if we tried to make the implementation of Fn generic (while still leaving the struct itself non-generic, to avoid cyclic types)?
struct GuessLoop;
impl<'a, F> FnOnce<(i32, &'a F)> for GuessLoop
where F: Fn(i32),
{
type Output = ();
extern "rust-call" fn call_once(self, args: (i32, &'a F)) -> Self::Output {
self.call(args)
}
}
impl<'a, F> FnMut<(i32, &'a F)> for GuessLoop
where F: Fn(i32),
{
extern "rust-call" fn call_mut(&mut self, args: (i32, &'a F)) -> Self::Output {
self.call(args)
}
}
impl<'a, F> Fn<(i32, &'a F)> for GuessLoop
where F: Fn(i32),
{
extern "rust-call" fn call(&self, (actual, recur): (i32, &'a F)) -> Self::Output {
let guess_int = rand::thread_rng().gen_range(1, 51);
if !try_guess(guess_int, actual) {
recur(actual)
}
}
}
Unfortunately, this fails to compile with the following error:
error[E0275]: overflow evaluating the requirement `<Fix<GuessLoop> as std::ops::FnOnce<(i32,)>>::Output == ()`
--> src/main.rs:99:5
|
99 | fix(GuessLoop)(secret_number);
| ^^^
|
= note: required because of the requirements on the impl of `for<'r> std::ops::Fn<(i32, &'r Fix<GuessLoop>)>` for `GuessLoop`
= note: required by `fix`
Essentially, the compiler is unable to verify that Fix<GuessLoop> implements Fn(i32), because in order to do that, it needs to verify that GuessLoop implements Fn(i32, &Fix<GuessLoop>), but that is only true if Fix<GuessLoop> implements Fn(i32) (because that impl is conditional), which is only true if GuessLoop implements Fn(i32, &Fix<GuessLoop>) (because that impl is conditional too), which... you get the idea. In order words, the two implementations of Fn here are dependent on each other, and the compiler is unable to resolve that.

Resources