Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make return type of the Fn traits an associated type #587

Merged
merged 6 commits into from
Jan 22, 2015

Conversation

nikomatsakis
Copy link
Contributor

@nikomatsakis nikomatsakis commented Jan 14, 2015

Change the return type of the Fn traits to be an associated type.

Rendered form.

cc rust-lang/rust#20871
cc rust-lang/rust#21019

@nikomatsakis nikomatsakis changed the title Add new RFC for fn return type Make return type of the Fn traits an associated type Jan 14, 2015
@Aatch
Copy link
Contributor

Aatch commented Jan 14, 2015

Maybe this should be .md file, not an .rs file?

@nikomatsakis
Copy link
Contributor Author

@Aatch um, yes.

...
}

impl<I,F> Iterator for Map<A,B>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be Map<I, F>?

@nikomatsakis
Copy link
Contributor Author

@huonw @quantheory thanks, corrected.

@ruuda
Copy link

ruuda commented Jan 19, 2015

This is maybe not related to this RFC, but it might have an impact on it, so I am posting it here. Currently it is impossible to do this

pub struct ComposedMut<X, Y, Z, F, G> {
    f: F,
    g: G
}

impl<X, Y, Z, F, G> FnMut(X) -> Z for ComposedMut<X, Y, Z, F, G>
where F: FnMut(Y) -> Z,
      G: FnMut(X) -> Y {
    extern "rust-call" fn call_mut(&mut self, arg: (X,)) -> Z {
        let (x,) = arg;
        let y = (self.g)(x);
        let z = (self.f)(y);
        z
    }
}

pub fn compose_mut<X, Y, Z, F, G>(f: F, g: G) -> ComposedMut<X, Y, Z, F, G>
where F: FnMut(Y) -> Z,
      G: FnMut(X) -> Y {
    ComposedMut { f: f, g: g }
}

due to a conflicting impl of FnMut in std::ops, even though the constraint on that impl is not satisfied. I was told on the IRC channel that this is because the impl is selected before the constraints are checked. Will this RFC allow the above impl? (The Fn analogue of this does work, fortunately.) If not, what prevents such an impl from being valid?

@nikomatsakis
Copy link
Contributor Author

@ruud-v-a This is indeed not really related to this RFC. Rather that is due to rust-lang/rust#19032. We do plan to to address this limitation although the exact best fix is not yet clear. It might be that negative impls are the best way forward.

@huonw
Copy link
Member

huonw commented Jan 21, 2015

Haskell has a trick to implement var-args (e.g. printf) by overloading the return value of a function, something like

trait Printf {
    fn method(...) -> Self;
}
impl Printf for String {
    fn method(...) -> String { /* do the printing */ }
}
impl<A, T: Printf> Printf for Box<Fn(A) -> T> {
    fn method(...) -> Box<Fn(A) -> T> { /* add an argument for formatting */ }
}

fn printf<T: Printf>(fmt: String) -> T { ... }

which gets used like let formatted: String = printf("%f %s")(1.0)("hello"); (with runtime checking that the types/counts actually match up); would this change restrict that?

@nikomatsakis
Copy link
Contributor Author

@huonw Those impls are fine. It would prevent something like:

impl<A,R> FnMut<A>  for F {
    type Output = R;
    ...
}

that is, having a closure type that can be invoked with any return at all, regardless of argument types, but I think that's pretty related.

@quantheory
Copy link
Contributor

Having considered for a while, I think that this RFC is really the best option overall, and it also accords with my intuition a bit better (that argument types are pretty free "inputs" that one can overload on, but result types need to depend on the arguments to some extent).

I suppose that this RFC prevents overloading based on lifetimes as well as normal types in the returned type, e.g. Fn() -> &'a str won't work. But I can't think of many uses for that, whereas wanting to connect the lifetime of an argument to the result is a pretty big deal.

@brson brson merged commit d60d776 into rust-lang:master Jan 22, 2015
@brson
Copy link
Contributor

brson commented Jan 22, 2015

Seems like a win except for the minor limitation about overloading based on lifetimes and return types. Feedback is positive. Accepted.

@ghost
Copy link

ghost commented Jan 25, 2015

Definitely welcome this change. For one, I believe it would allow for a generic implementation of partial application for closures. Along with other recent changes (associated types for operators), that would really improve the ergonomics for functional programming. I can see a number of areas where I could make good use of such a change to improve my libraries.

@ruuda
Copy link

ruuda commented Jan 30, 2015

Now this has landed, I ran into some issues with it. A minimal example is the following:

trait FnBuilder<T> {
   type F: Fn<(T,)>;
   fn get(&self) -> Self::F;
}

fn iterate<T, B: FnBuilder<T>>(b: B, mut t: T) -> T {
    let function = b.get();
    for _ in 0i32 .. 42 {
        t = function(t);
    }
    t
}

This could be expressed before, but not any more: the compiler complains that the return type of function is an associated type, and not the type parameter T. However, the following does not work either:

trait FnBuilder<T> {
   type F: Fn(T) -> T;
   fn get(&self) -> Self::F;
}

fn iterate<T, B: FnBuilder<T>>(b: B, mut t: T) -> T {
    let function = b.get();
    for _ in 0i32 .. 42 {
        t = function(t);
    }
    t
}

Is it still possible to express the bound that <B::F as Fn<(T,)>>::Output == T? A where clause with == is not supported, right?

@japaric
Copy link
Member

japaric commented Jan 30, 2015

@ruud-v-a This compiles:

#![crate_type = "lib"]

trait FnBuilder<T> {
   type Fn: Fn(T) -> T;

   fn get(&self) -> Self::Fn;
}

fn iterate<T, F: Fn(T) -> T, B: FnBuilder<T, Fn=F>>(b: B, mut t: T) -> T {
    let function = b.get();
    for _ in 0i32 .. 42 {
        t = function(t);
    }
    t
}

Not sure why you need to "duplicate" the F: Fn(T) -> T bound on the iterate function. It could be a type inference regression (if it worked without it before this landed), or it could be that bounds on associated types is still not fully baked (I haven't played much with this feature, so I can't confirm).

@bgamari
Copy link

bgamari commented Jan 30, 2015

@ruud-v-a @japaric this sounds like #21636.

@Centril Centril added A-traits-libstd Standard library trait related proposals & ideas A-associated-types Proposals relating to associated types labels Nov 23, 2018
@Centril Centril added the A-closures Proposals relating to closures / lambdas. label Nov 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-types Proposals relating to associated types A-closures Proposals relating to closures / lambdas. A-traits-libstd Standard library trait related proposals & ideas
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants