In Rust, is there any way to use traits and impls to (recursively) flatten tuples?
If it helps, something that works with N nested pairs is a good start
trait FlattenTuple {
fn into_flattened(self) -> /* ??? */
}
// such that
assert_eq!((1, (2, 3)).into_flattened(), (1, 2, 3))
It would be even better if it could be extended work with any kind of nested tuple such that:
assert_eq!(((1, 2), 2, (3, (4, 5))).into_flattened(), (1, 2, 2, 3, 4, 5))
Maybe for certain small definitions of "flatten", but realistically not really.
Start with the most specific implementation:
trait FlattenTuple {
fn into_flattened(self) -> (u8, u8, u8);
}
impl FlattenTuple for (u8, (u8, u8)) {
fn into_flattened(self) -> (u8, u8, u8) {
(self.0, (self.1).0, (self.1).1)
}
}
Then make it a bit more generic:
trait FlattenTuple {
type Output;
fn into_flattened(self) -> Self::Output;
}
impl<A, B, C> FlattenTuple for (A, (B, C)) {
type Output = (A, B, C);
fn into_flattened(self) -> Self::Output {
(self.0, (self.1).0, (self.1).1)
}
}
And then repeat for every possible permutation:
impl<A, B, C, D, E, F> FlattenTuple for ((A, B), C, (D, (E, F))) {
type Output = (A, B, C, D, E, F);
fn into_flattened(self) -> Self::Output {
((self.0).0, (self.0).1, self.1, (self.2).0, ((self.2).1).0, ((self.2).1).1)
}
}
These two implementations cover your two cases.
However, you'd then have to enumerate every input type you'd like, probably via code generation. There's no way I'm aware of to "inspect" the input type and then "splice" it into the output type.
You can even try to write something somewhat recursive:
impl<A, B, C, D, E, F> FlattenTuple for (A, B)
where A: FlattenTuple<Output = (C, D)>,
B: FlattenTuple<Output = (E, F)>,
{
type Output = (C, D, E, F);
fn into_flattened(self) -> Self::Output {
let (a, b) = self;
let (c, d) = a.into_flattened();
let (e, f) = b.into_flattened();
(c, d, e, f)
}
}
But this will quickly run into base-case issues: the terminal 42 doesn't implement FlattenTuple, and if you try to impl<T> FlattenTuple for T you will hit conflicting trait implementations.
Related
I'm in the following situation:
fn some_fn<K, T, F, S>(func: F, other_func: S) -> Vec<i64>
where
K: SomeType<T>,
T: SomeOtherType,
F: Fn() -> (),
S: Fn() -> (),
{
//...
}
For the above example, Rust can correctly infer types T, F, and S, but not K (as is expected).
Is there a way to only specify the type of K when calling some_fn without also specifying T, F, and S?
My current workaround is to change the signature to some_fn to fn some_fn<K, T, F, S>(cheat: Option<K>, func: F, other_func: S) and call the function like so:
let cheat: Option<SomethingThatImplements> = None;
let result = some_fn(cheat, func, other_func);
However, I find this to be very clunky. I haven't been able to find anything regarding this topic, is it even possible to specify only part of the type args?
Yes. Use _ for the arguments you want to infer:
some_fn::<Foo, _, _, _>(...);
I was trying some code from the tutorial.
I followed exactly what the tutorial did, a return statement without return and got the following error: expected tuple, found ().
I added the return, then it works. What's happening?
fn reverse(pair: (i32, bool)) -> (bool, i32)
{
let (a, b) = pair;
return (b, a); // No error
// (b, a); // Error
}
fn main() {
let pair = (1, true);
println!("Pair: {:?}", pair);
println!("Reversed: {:?}", reverse(pair))
}
The () is a unit type in rust and it is used as a nothing type, similar to void in c/c#/java.
Adding a semicolon the end of an experssion wil tell the compiler to disregard its value and resolve it to unit. So (b, a); has the type of unit when returning its value from the function.
Removing the semicolon will stop the suppression of the expression and return the tuples value.
fn reverse(pair: (i32, bool)) -> (bool, i32){
let (a, b) = pair;
(b, a)
}
If you include the ; (semicolon), it's treated as a complete statement. It will not automatically return the value you expect - it will return () (unit type). If you omit the semicolon, then it will return that value.
fn reverse(pair: (i32, bool)) -> (bool, i32)
{
let (a, b) = pair;
(b, a) // return value
}
The sample does omit the semicolon FYI.
I was told that Rust has a semantics in affine logic -- so one has deletion/weakening but not duplication/contraction.
The following compiles:
fn throw_away<A, B>(x: A, _y: B) -> A {
x
}
Because duplication is disallowed, the following does not compile:
fn dup<A>(x: A) -> (A, A) {
(x, x)
}
Similarly, neither of these compile:
fn throw_away3<A, B>(x: A, f: fn(A) -> B) -> A {
x;
f(x)
}
fn throw_away4<A, B>(x: A, f: fn(A) -> B) -> A {
throw_away(x, f(x))
}
Weakening is also witnessable
fn weaken<A, B, C>(f: fn(A) -> B) -> impl Fn(A, C) -> B {
move |x: A, y: C| f(x)
}
Instead of returning fn(A, C) -> B, we returned impl Fn(A, C) -> B. Is there a way to return fn(A, C) -> B instead? It's fine if not; I'm just curious.
Something else I expect is that you can lift A to () -> A. However, functions in Rust can be duplicated and used more than once. For example,
fn app_twice(f: fn(A) -> A, x: A) -> A {
f(f(x))
}
Suppose there was actually a function lift(x: A) -> fn() -> A, then we could break the move semantics. For example, this would allow
fn dup_allowed(x: A) -> (A, A) {
let h = lift(x);
(h(), h())
}
Thus to lift A to fn() -> A, we need to know that the function is "linear/affine" or can be used only once. Rust provides a type for this: FnOnce() -> A. In the following, the first compiles, and the second does not.
fn app_once(f: impl FnOnce(A) -> A, x: A) -> A {
f(x)
}
fn app_twice2(f: impl FnOnce(A) -> A, x: A) -> A {
f(f(x))
}
The following functions are inverses of each other (probably, I don't know Rust's semantics well enough to say that they are actually inverse to each other):
fn lift_up<A>(x: A) -> impl FnOnce() -> A {
move || x
}
fn lift_up_r<A>(f: impl FnOnce() -> A) -> A {
f()
}
Since fn dup<A>(x: A) -> (A, A) { (x,x) } does not compile, I thought that the following might be a problem:
fn dup<A>(x: fn() -> A) -> (A, A) {
(x(), x())
}
It seems that Rust is doing something special for fn(A) -> B types.
Why don't I have to declare that x is reusable/duplicable in the above?
Perhaps something different is going on. Declared functions are a bit special fn f(x: A) -> B { ... } is a particular witness that A -> B. Thus if f needs to be used multiple times, it can be reproved as many times as needed, but fn(A) -> B is a completely different thing: it is not a constructed thing but a hypothetical thing, and must be using that fn(A) -> Bs are duplicatable. In fact, I've been thinking that it's more like a freely duplicable entity. Here's my rough analogy:
fn my_fun<A,B>(x :A) -> B { M } "is" x:A |- M:B
fn(A) -> B "is" !(A -o B) hence freely duplicable
Thus fn() -> A "is" !(() -o A) = !A hence fn () -> A is the (co)free duplication on A
fn dup_arg<A: Copy>(x: A) -> B { M } "says" that A has duplication or is a comonoid
impl FnOnce (A) -> B "is" A -o B
But this can't be right... For what is impl Fn(A) -> B? From playing around a bit, it seems that fn(A) -> B is more strict than Fn(A) -> B. What am I missing?
fn weaken<A, B, C>(f: fn(A) -> B) -> impl Fn(A, C) -> B {
move |x: A, y: C| f(x)
}
Instead of returning fn(A, C) -> B, we returned impl Fn(A, C) -> B. Is there a way to return fn(A, C) -> B instead? It's fine if not; I'm just curious.
No, because a fn is by definition not a closure: it cannot contain any state that wasn't compiled into the program (in this case, the value of f). This is closely related to your next observation: because a fn cannot close over anything, it trivially cannot contain any non-Copy types and therefore can always be called multiple times, or itself copied, without violating the properties we're discussing.
Precisely: all fn(..) -> _ types implement Fn and Copy (as well as FnOnce).
Copy is the marker trait ('marker' meaning it provides no methods) that has the special purpose of telling the compiler that it is free to copy the bits of a type automatically whenever it is used more than once. Anything implementing Copy is opting out of the move-but-not-copy system -- but can't thereby violate the non-Copy-ness of a different type.
Fn is the trait for functions that can be called by immutable reference (not modifying or consuming the function itself). This is in principle separate from Copy, but it's very similar in effect; the differences that one could end up with (some of these can't happen in ordinary code) are:
If a function implements Fn but not Copy or Clone, then you can't store the function multiple places but you can call it as many times as you want.
If a function implements Copy but not Fn (only FnOnce), then this is invisible because every call of it (except for the last) implicitly copies it.
If a function implements Clone but not Fn or Copy, then you would have to .clone() it each time you called it (except the last).
And indeed the following functions are inverses of eachother (probably, I don't know rust's semantics well enough to say that they are actually inverse to each other):
fn lift_up<A> (x:A) -> impl FnOnce () -> A {move | | x}
fn lift_up_r<A> (f : impl FnOnce () -> A) -> A {f()}
lift_up_r accepts functions that lift_up did not produce; for example, if f has a side effect, panics, or hangs then let f = lift_up(lift_up_r(f)); has that effect. Ignoring that, they are inverses. An even better pair of inverses without that caveat would be functions for moving a value into a struct and back out -- which this is effectively doing, except for allowing inputs that aren't of that particular struct type.
Since fn dup (x:A) -> (A,A) {(x,x)} does not compile, I thought that the following might be a problem:
fn dup<A> (x : fn() -> A) -> (A,A) {(x(),x()}
But it seems that rust is doing something special for fn(A) -> B types. Finally, my question: why don't I have to declare that x is reusable/duplicable in the above?
When you have a generic function with a type variable, fn dup<A>, the compiler makes no assumptions about the properties of A (except that it is Sized unless you opt out of that implicit bound, because working with non-Sized values is highly restrictive and usually not what you want). In particular, it does not assume that A implements Copy.
On the other hand, as I mentioned above, all fn types implement Fn and Copy, so they can always be duplicated and reused.
The way to write a dup function which operates on general functions and fails to compile in the way you expect is:
fn dup<A, F>(x: F) -> (A,A)
where
F: FnOnce() -> A
{
(x(),x())
}
Here, we tell the compiler that F is a type of function which is consumed by calling it, and don't tell it about any way to duplicate F. So, it fails to compile with "error[E0382]: use of moved value: x". The shortest way to make this compile would be to add the bound F: Copy, and the most general would be to add F: Clone and an explicit .clone() call.
Perhaps something different is going on. Declared functions are a bit special fn f(x:A) -> B {...} is a particular witness that A -> B. Thus if f needs to be used multiple times, it can be reproved as many times as needed. But fn(A) -> B is a completely different thing: it is not a constructed thing but a hypothetical thing, and must be using a that fn(A) -> Bs are duplicatable. In fact, I've been thinking that it's more like a freely duplicable entity.
I'm no logician, but I think that the first half of this is not correct. In particular, (outside of some irrelevant considerations about generics) there are no properties that "a declared function" has that an arbitrary value of type fn(A) -> B does not have. Rather, the value of type fn(A) -> B can be copied, and that copiability corresponds directly to the fact that "it can be reproved", because (until we start introducing ideas like JIT code generation) every value of type fn(A) -> B refers to a compiled piece of code (and no other data) -- and hence a lemma that the compiler has checked and given the program license to reuse it as many times as needed at run time.
For what is impl Fn(A) -> B? From playing around a bit, it seems that fn(A) -> B is more strict than Fn(A) -> B. What am I missing?
The impl syntax serves different roles, but in argument position it is almost exactly a shorthand for generics. If I write
fn foo<A, B>(f: impl Fn(A) -> B) {}
then that is equivalent to
fn foo<A, B, F>(f: F)
where
F: Fn(A) -> B
{}
except that the caller is not allowed to specify any of the parameters when any impl argument types exist (this is not relevant to your interests but I mention it for accuracy). Thus, we're telling the compiler that F can be anything as long as it is callable as a reusable function. In particular, we're not specifying F: Copy or F: Clone. fn(A) -> B, on the other hand, is a concrete type which implements Fn(A) -> B and Copy, so you get that for free.
In return position, fn ... -> impl Fn(A) -> B, the impl denotes an existential type: you're asserting that there exists some type implementing Fn which the function will return. The compiler tracks the concrete type in order to generate code, but your program avoids naming it. This is necessary when returning a closure, but optional when returning a function that does not close over anything: for example, you can write
fn foo<A>() -> fn(A) -> A {
|x| x
}
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`
This small FizzBuzz program using unboxed closures gives a rather mysterious error message.
fn fizzbuzz<F: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: F, bs: &str) {
for i in range(1i64, n+1) {
match (f(i), b(i)) {
(true, true) => println!("{:3}: {}{}", i, fs, bs),
(true, _) => println!("{:3}: {}", i, fs),
(_, true) => println!("{:3}: {}", i, bs),
_ => (),
}
}
}
fn main() {
fizzbuzz(30,
|&: i: i64| { i % 3 == 0 }, "fizz",
|&: j: i64| { j % 5 == 0 }, "buzz");
}
Error message:
<anon>:15:14: 15:40 error: mismatched types: expected `closure[<anon>:14:14: 14:40]`, found `closure[<anon>:15:14: 15:40]` (expected closure, found a different closure)
<anon>:15 |&: j: i64| { j % 5 == 0 }, "buzz");
^~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
Could someone describe the error please? Thanks.
This piece of code demonstrates the essence of the problem:
fn show_both<S: Show>(x: S, y: S) {
println!("{} {}", x, y);
}
You can only call it with both arguments of the same type, i.e. this is allowed:
let x: i32 = 10;
let y: i32 = 20;
show_both(x, y);
But this is not:
let x: i32 = 10;
let y: f64 = 20.0;
show_both(x, y);
This is fairly natural: you specified that both of the parameters must be of the same type, even though this type can be arbitrary as long as it implements Show.
You have essentially the same thing in your code:
fn fizzbuzz<F: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: F, bs: &str)
You declared that both f and b must have the same type. However, for each unboxed closure the compiler generates a different type - this is quite natural as well, given that different closures can capture different variables.
You need to specify different type parameters in order to be able to pass different closures:
fn fizzbuzz<F1, F2>(n: i64, f: F1, fs: &str, b: F2, bs: &str)
where F1: Fn(i64) -> bool,
F2: Fn(i64) -> bool
Each unboxed closure definition creates a completely different type. This makes each of the closures defined in main a different type. Your fizzbuz function, on the other hand, requires each of the closures passed to it to be the same type, F. If you change fizzbuzz's signature to:
fn fizzbuzz<F: Fn(i64) -> bool, G: Fn(i64) -> bool>(n: i64, f: F, fs: &str, b: G, bs: &str)
your code will typecheck.
Basically, the <F: Fn(i64) -> bool> syntax doesn't create a wildcard for types that implement the trait parameter (Fn(i64) -> bool), but declares a single type which must satisfy the trait parameter and be the same type everywhere it is used. Unboxed closure definitions must be different types both because the may wrap different environments but also because they dispatch to different functions (that is, each has a different body). As a result, fizzbuzz needs two different type parameters to accommodate the two closure types.