Modify and return closure - rust

I'm trying to make a function to do the following:
accept a closure f of the form fn(T) -> T
return a closure of the form fn(T, bool) -> T, that conditionally performs f depending on the bool parameter.
I come from a haskell-ish background, and in Haskell this would be something like this:
conditionally :: (a -> a) -> a -> Bool -> a
conditionally f x True = f x
conditionally f x False = x
Translating that to something more rust-like:
conditionally :: ((t) -> t) -> ((t, Bool) -> t)
conditionally f = \(x, b) -> if b then (f x) else (x)
I tried the following in rust:
fn conditionally<T>(f: &'static (dyn Fn(T) -> T + 'static)) -> Box<dyn Fn(T, bool) -> T> {
Box::new(&|x, b| if b { f(x) } else { x } )
}
and was helpfully told to use the move keyword to ensure the closure takes ownership of f. However, the following still doesn't work:
fn conditional<T>(f: &'static (dyn Fn(T) -> T + 'static)) -> Box<dyn Fn(T, bool) -> T> {
Box::new(&move|x, b| if b { f(x) } else { x } )
}
I'm getting the following error (this also appeared prior to adding move):
error[E0515]: cannot return value referencing temporary value
--> src/main.rs:216:5
|
216 | Box::new(&move|x, b| if b { f(x) } else { x } )
| ^^^^^^^^^^-----------------------------------^^
| | |
| | temporary value created here
| returns a value referencing data owned by the current function
I'm thinking that the 'data owned by the current function' is either the closure I've defined, or the f that I've moved, but I can't get my head around how it all fits together.
As a smoke check, I made sure I'm able to box simpler closures that I define in the function body, and the following compiles:
fn conditional_increment() -> Box<dyn Fn(i32, bool) -> i32> {
Box::new(&|x, b| if b { x + 1 } else { x } )
}
What am I missing here? Is this possible in rust?
I'm also wondering if there's a more specific name for what I'm trying to do than simply higher-order functions, as I'm struggling to find resources on this kind of problem.
Update: I've realised that "currying in rust" would have been a good term to search for. While this isn't an example of currying, it would use the same language features, and would have led me to the answer given by vallentin.

You're attempting to return a boxed reference to a closure defined within the conditional function. Which you can't do, given that the closure only lives for the duration of the call. Instead you can return the closure itself, and in short just have to remove the &, i.e. turn &move |x, b| ... into move |x, b| ....
fn conditional<T>(f: &'static (dyn Fn(T) -> T + 'static)) -> Box<dyn Fn(T, bool) -> T> {
Box::new(move |x, b| if b { f(x) } else { x })
}
However, the more idiomatic way to write what you're attempting, which is to use generics and a type parameter for the closure. Check out:
Rust By Example - (Closures) As input parameters
Rust By Example - (Closures) As output parameters.
In short, you could rewrite it into this:
fn conditional<F, T>(f: F) -> Box<dyn Fn(T, bool) -> T>
where
F: Fn(T) -> T + 'static,
T: 'static,
{
Box::new(move |x, b| if b { f(x) } else { x })
}
You can actually also do without the box, and use the impl Trait syntax.
fn conditional<F, T>(f: F) -> impl Fn(T, bool) -> T
where
F: Fn(T) -> T,
{
move |x, b| if b { f(x) } else { x }
}
You can also use the impl Trait syntax for parameters, which the links shows, but personally I find it to noisy when dealing with closuring.
Using it the boils down to something as simple as this:
let f = conditional(move |x| x * 2);
println!("{}", f(2, false)); // Prints `2`
println!("{}", f(2, true)); // Prints `4`

Related

Currying syntax in rust

I am trying to write a curried function in rust. According to this discussion it should be possible, though it doesn't work for me.
For example this is an implementation of B combinator in java:
static <X, Y, Z> Function<Function<X, Y>, Function<X, Z>> b(Function<Y, Z> f) {
return g -> x -> f.apply(g.apply(x));
}
I am trying to do a similar thing in rust:
fn b<F, G, X, Y, Z>(f: F) -> impl Fn(G) -> impl Fn(X) -> Z
where F: Fn(Y) -> Z,
G: Fn(X) -> Y,
{
move |g: G| move |x: X| f(g(x))
}
But getting the following error.
error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in `Fn` trait return
Do I do it wrong?

return closures but cannot infer type

When learning rust closures,I try Like Java return "A Function"
fn equal_5<T>() -> T
where T: Fn(u32) -> bool {
let x:u32 = 5;
|z| z == x
}
But when i use it
let compare = equal_5();
println!("eq {}", compare(6));
Build error
11 | let compare = equal_5();
| ------- consider giving `compare` a type
12 | println!("eq {}", compare(6));
| ^^^^^^^^^^ cannot infer type
|
= note: type must be known at this point
See: https://doc.rust-lang.org/stable/rust-by-example/trait/impl_trait.html
Currently T simply describes a type, which in this case implements the Fn trait. In other words, T isn't a concrete type. In fact with closures, it's impossible to declare a concrete type because each closure has it's own unique type (even if two closures are exactly the same they have different types.)
To get around directly declaring the type of the closure (which is impossible) we can use the impl keyword. What the impl keyword does is convert our description of a type (trait bounds) into an invisible concrete type which fits those bounds.
So this works:
fn equal_5() -> impl Fn(u32) -> bool {
let x:u32 = 5;
move |z| z == x
}
let compare = equal_5();
println!("eq {}", compare(6));
One thing to note is we can also do this dynamically. Using boxes and the dyn trait. So this also works, however incurs the associated costs with dynamic resolution.
fn equal_5() -> Box<dyn Fn(u32) -> bool> {
let x:u32 = 5;
Box::new(move |z| z == x)
}
let compare = equal_5();
println!("eq {}", compare(6));
The compiler seems to be complaining that it's expecting a type parameter but finds a closure instead. It knows the type, and doesn't need a type parameter, but also the size of the closure object isn't fixed, so you can either use impl or a Box. The closure will also need to use move in order to move the data stored in x into the closure itself, or else it wont be accessible after equal_5() returns, and you'll get a compiler error that x doesn't live long enough.
fn equal_5() -> impl Fn(u32) -> bool {
let x:u32 = 5;
move |z| z == x
}
or
fn equal_5() -> Box<Fn(u32) -> bool> {
let x:u32 = 5;
Box::new(move |z| z == x)
}

How to do Higher Order Composition in Rust?

I have this function that works:
fn compose<A, B, C>(f: impl Fn(A) -> B, g: impl Fn(B) -> C) -> impl Fn(A) -> C {
move |x: A| g(f(x))
}
I'd like to use currying in this situation, so that I can do:
/* THIS */
compose(f)(g)(x)
/* Instead of this */
compose(f, g)(x)
Is it even reasonable to think about making a macro such that this is possible?
compose(f)(g)(h)ยทยทยท(x)
I've managed to get something that's kinda similar to what I want:
fn compose<A, B, C, F: 'static, G: 'static>(f: F) -> impl Fn(G) -> Box<dyn Fn(A) -> C>
where
F: Fn(A) -> B + Copy,
G: Fn(B) -> C + Copy,
{
move |g: G| Box::new(move |x: A| g(f(x)))
}
let inc = |x| x + 1;
let mult2 = |x| x * 2;
let inc_compose = compose(inc);
println!("{}", inc_compose(mult2)(3)); // 8
Now there's a new problem: when creating a higher-order function based on my compose function, I need to give the function a type dependent on another function:
let inc = |x: usize| x + 1;
let mult2 = |x: usize| x * 2;
let mult3 = |x: usize| x * 3;
let inc_compose = compose(inc);
println!("{}", inc_compose(mult2)(3)); // 8
println!("{}", inc_compose(mult3)(3)); // ERROR: [rustc E0308] [E] mismatched types
Is there a way to avoid this error?
None of these similar posts answer my question:
How do I emulate Lisp (apply) or (curry) in Rust?
How to implement a multiple level currying function in Rust?
How to invoke a multi-argument function without creating a closure?
The main reason is that I want currying to get the ability to do point-free programming.
In Rust, currying is hard. The strict type system, lifetimes, and nested impl traits not existing all conspire to ruin your day when you try. However, it is possible to create a macro to curry functions, and I did so for code golf at one point. The ungolfed version is here:
macro_rules! curry{
(
$f:ident
$(
($args:ident : $types:ty)
)*
) => {
$(move |$args: $types|)*
$f(
$($args),*
)
}
}
This matches on expressions of the form name(arg1, type1)(arg2, type2)...(argN, typeN) and returns a chain of move closures leading to the function call. Most of the time, however, you can just use _ for the type and let inference figure it out. Using it with compose is simple:
let inc = |x| x as u32 + 1;
let double = |x| x * 2;
let curried = curry!(compose(f: _)(g: _));
assert_eq!(curried(inc)(double)(7), 16);
The code generated by the macro, in this case, looks like this:
move |f: _| move |g: _| compose(f, g)
It simply takes the supplied name and type and pastes it into the closure heads, so you can use it to force arguments for generic functions into concrete types.
Playground

Syntax issue function returns function not clear

I'm getting confused by this.
If the following works:
fn func_exit() -> bool {
println!("hi");
true
}
fn locate_func() -> fn() -> bool {
func_exit
}
Why these following syntaxes throw error?
fn locate_func1<F: Fn() -> bool>() -> F {
func_exit
}
fn locate_func2<F>() -> F where F:Fn() -> bool {
func_exit
}
I'm new to Rust and the following error message is not clear to me:
error[E0308]: mismatched types
--> src/main.rs:44:9
|
43 | fn locate_func1<F: Fn() -> bool>() -> F {
| - this type parameter - expected `F` because of return type
44 | func_exit
| ^^^^^^^^^ expected type parameter `F`, found fn item
|
= note: expected type parameter `F`
found fn item `fn() -> bool {func_exit}`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
error[E0308]: mismatched types
--> src/main.rs:47:9
|
46 | fn locate_func2<F>() -> F where F:Fn() -> bool {
| - - expected `F` because of return type
| |
| this type parameter
47 | func_exit
| ^^^^^^^^^ expected type parameter `F`, found fn item
|
= note: expected type parameter `F`
found fn item `fn() -> bool {func_exit}`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
Appreciate some help.
Thanks
fn locate_func() -> fn() -> bool {
func_exit
}
locate_func returns a pointer to a function returning bool (i.e. fn() -> bool).
fn locate_func1<F: Fn() -> bool>() -> F {
func_exit
}
locate_func1 means that the caller can specify any F satisfying the bounds Fn() -> bool, and it will return an F. But it is clearly not the case that func_exit is necessarily of the type specified by the caller.
locate_func2 has the same problem, just with where-Notation.
What you probably want could be the following:
fn locate_func3() -> impl Fn() -> bool {
func_exit
}
It says: locate_func3 returns something that implements the bound Fn() -> bool (and func_exit does so, so it can be returned there).
Also note the difference between fn (function pointers) and Fn ("something callable"):
The error is weird, but... you can't just return a bounded value like this, because bounded types are decided by the caller, and it doesn't make sense for the caller to decide what the return type is unless that return type is a consequence of an input type (e.g. an identity function is a trivial example of this). Which is not the case here.
If you want to return something generic, you need either some sort of trait object (so e.g. box the function and return a Box) or an impl (which still has a concrete return value but hides it at the API level).
The latter is more efficient but only allows returning a single concrete type, the former is less efficient but lets you return e.g. different closures.

How to do things equal to nested `impl Trait`?

function A which take a function B as parameter, again the function B take function C as parameter. I try the syntax like below, but this gives me an error:
fn a(b: impl Fn(impl Fn() -> ()) -> ()) -> () {
// ...
}
error[E0666]: nested `impl Trait` is not allowed
--> src/main.rs:2:21
|
2 | fn a(b: impl Fn(impl Fn() -> ()) -> ()) -> () {
| --------^^^^^^^^^^^^^^^-------
| | |
| | nested `impl Trait` here
| outer `impl Trait`
For some reason, I can't use &dyn keyword:
fn a(b: impl Fn(&dyn Fn() -> ()) -> ()) -> () {
// ...
}
Are there another ways to do this?
And I don't know why nested impl Trait cause an error.
Nested impl Trait doesn't work because it hasn't been specified or implemented yet. Also, because it isn't very useful given how other parts of Rust work.
fn a(b: impl Fn(impl Fn() -> ()) -> ()) -> () can be written using full generic syntax as
fn a<B, C>(b: B) -> ()
where B: Fn(C) -> (),
C: Fn() -> ()
So what does that mean? It means that B is something callable that takes an argument of something else callable. But the important thing here is in the concrete types. Specifically, B doesn't take any callable with a compatible signature, but specifically a C. What is C? Well, that's the issue. If you call a like this:
a(|f| f());
then C is the type of the lambda's parameter f, but that type is not known, since the parameter f's type cannot be deduced from usage alone. But suppose that wasn't a problem, what would the body of a look like?
fn a<B, C>(b: B) -> ()
where B: Fn(C) -> (),
C: Fn() -> () {
b(|| ())
}
Here we attempt to call b passing lambda. But the lambda's type is unnamed local lambda type, not C. Since C was passed in from the outside, it cannot be the type of a lambda local to a. Simply put, unless you pass in a C as an additional parameter to a, there is nothing that a has that it could pass to b.
What you apparently want is for B to be not a function object that can be called with some C, but with any C. You want it to be a polymorphic function object. Rust doesn't support compile-time polymorphic function objects (the equivalent in Haskell would be a forall a. a -> IO () or similar). It only supports runtime polymorphic function objects.
That's the job of dyn. Now you've said you can't use &dyn, because you want to pass the function object to another thread. So instead, use Box<dyn>:
fn a(b: impl Fn(Box<dyn Fn() -> ()>) -> ()) -> () {
b(Box::new(|| println!("Hello")))
}
fn main() {
a(move |f| { f(); f(); });
}

Resources