Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upGeneric closures #1650
Conversation
This comment has been minimized.
This comment has been minimized.
|
Can you make the closure generic over lifetimes as well? Are there any potential ambiguities with this syntax? Should we have the generics introduced with the |
sfackler
reviewed
Jun 15, 2016
| The generated closure type will have generic implementations of `Fn`, `FnMut` and `FnOnce` with the provided type bounds. This is similar to the way closures currently have generic implementations over lifetimes. | ||
|
|
||
| # Drawbacks | ||
| [drawbacks]: #drawbacks |
This comment has been minimized.
This comment has been minimized.
sfackler
Jun 15, 2016
Member
I am suspicious of any proposal that has zero drawbacks, alternatives, and unanswered questions.
sfackler
reviewed
Jun 15, 2016
| There are two ways to specify generic bounds on closures: | ||
|
|
||
| ```rust | ||
| <T: Debug>|x: T| println!("{:?}", x); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Amanieu
Jun 15, 2016
Author
Contributor
From the discussions on IRC, it seems that higher-ranked inference is very hard and/or undecidable. I went for the conservative option of requiring bounds for generic closures.
This comment has been minimized.
This comment has been minimized.
durka
Jun 15, 2016
Contributor
Rust has never inferred generic bounds, that sounds like C++ :)
On Jun 15, 2016 2:30 PM, "Steven Fackler" notifications@github.com wrote:
In text/0000-generic-closures.md
#1650 (comment):
+let b = Tuple(1u8, 2u32, 3.5f32).fold(10.0, <T: Into>|x, y: T| x + y.into());
+assert_eq!(b, 16.5f64);
++ +A fully working example of this code (with manually implemented closures) can be found [here](https://play.rust-lang.org/?gist=ea867336945253752e31873fc752ec06&version=nightly&backtrace=0). + +# Detailed design +[design]: #detailed-design + +## Syntax + +There are two ways to specify generic bounds on closures: + +rust
+<T: Debug>|x: T| println!("{:?}", x);Are these bounds required? Can they be inferred?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/rust-lang/rfcs/pull/1650/files/82b65b9bf80f6cecbded445c08df5560c359a6df#r67218397,
or mute the thread
https://github.com/notifications/unsubscribe/AAC3n6w3dDkkTfbTArkT4MoVbsMItwkZks5qMETEgaJpZM4I2Wcq
.
This comment has been minimized.
This comment has been minimized.
Of course. Note that this is already possible: |x: &(i32, i32)| -> &i32 { &x.0 }This closure is generic over all possible lifetimes for the input reference.
There should be no ambiguities. It is already possible to invoke a function using the |
This comment has been minimized.
This comment has been minimized.
|
Where do we put the move <T: Debug> |x: T| ...
<T: Debug> move |x: T| ... |
This comment has been minimized.
This comment has been minimized.
|
I doubt the feasibility here is that generic functions can't be values. |
This comment has been minimized.
This comment has been minimized.
|
@F001 The closure only has a single type, it's just that it implements the |
This comment has been minimized.
This comment has been minimized.
|
Would this let you do: let foo = <T: Debug>|x: T| println!("{:?}", x);
foo(42);
foo("bar");(I'm guessing no.) |
This comment has been minimized.
This comment has been minimized.
|
@durka Yes it would. |
This comment has been minimized.
This comment has been minimized.
|
I see. The generic type parameter can only used in the arguments, not the captured values. |
This comment has been minimized.
This comment has been minimized.
|
@kennytm The |
This comment has been minimized.
This comment has been minimized.
|
That's really neat. I'm fully in favor of this. |
This comment has been minimized.
This comment has been minimized.
|
Functionality-wise I agree that this would be a good thing to have. The proposed syntax strikes me as arcane and haphazard, although I'm not sure whether it's possible to do any better. Two things in particular bother me about it:
(The syntax for specifying closure types It's interesting that if we wanted to be able to write a type annotation for a generic closure, we'd need #518 (just like for non-generic closures), and #1481 as well (which was mentioned above). If we did have both of those, another way to write a generic closure would be Takeaway: this seems closely related #1481 in terms of both syntax and functionality, so maybe there should be some cross-pollination going on between them. |
This comment has been minimized.
This comment has been minimized.
|
@glaebhoerl does it conflict with potential HKT syntax? (or is this just HKT-for-closures as it is?) |
This comment has been minimized.
This comment has been minimized.
|
HKT is totally separate from this I think. It's "higher-ranked trait bounds quantified over types and not just lifetimes" (iow, " |
This comment has been minimized.
This comment has been minimized.
|
Right. So... if we end up using |
This comment has been minimized.
This comment has been minimized.
comex
commented
Jun 16, 2016
|
I don't think it would be confusing, because the meaning matches up. The expression |
This comment has been minimized.
This comment has been minimized.
We wouldn't. |
This comment has been minimized.
This comment has been minimized.
tikue
commented
Jun 21, 2016
|
Can the text of this RFC please mention some alternative syntaxes that don't have the lookahead problem? I feel the same as @sfackler that the lack of drawbacks/alternatives is not duly diligent. |
aturon
added
the
T-lang
label
Jun 21, 2016
This comment has been minimized.
This comment has been minimized.
It's interesting to note what it could look like with the let f: some Fn(any Debug) = |x| println!("{:?}", x);Or in a call: foo(|x: any Debug| println!("{:?}", x));This is potentially a stronger argument for the |
This comment has been minimized.
This comment has been minimized.
|
Noooooo don't pull this RFC thread into that neverending one. But anyway, that syntax doesn't seem fully general since you can't refer to On Tue, Jun 21, 2016 at 9:54 PM, Scott Olson notifications@github.com
|
This comment has been minimized.
This comment has been minimized.
|
@durka Sorry.
This, of course, is something that could be added after this RFC is accepted, but it might make some people feel less worried about the syntax proposed by this RFC if a future shorthand is possible. |
This comment has been minimized.
This comment has been minimized.
|
|
This comment has been minimized.
This comment has been minimized.
|
+1 for this RFC. I'm in a situation today where I have a closure which is generic over a lifetime and I need to refer to that lifetime explicitly, but there's no syntax to do that. In other words I have this: Just adding this would be a good first step and would be simpler than adding closures which are generic over types aswell. |
This comment has been minimized.
This comment has been minimized.
|
@canndrew In fact, we support that today, if the closure is an argument to a generic function which has a |
This comment has been minimized.
This comment has been minimized.
|
@Amanieu It should be explicit that the generic parameters have to be used in the argument types, i.e. the rule that prevents However, there's an interesting point to made here: Where you actually end up is the distinction between early-bound and late-bound parameters which @nikomatsakis has discussed mostly for lifetimes. For closures, allowing parameters that you have to explicitly supply seems pointless, at least until we have HKT, which would let you abstract over such closure types to some extent. OTOH, being able to use generic functions like generic closures, at least over their late-bound type parameters (if they weren't inferred to anything specific), can be an useful extension to this RFC. |
This comment has been minimized.
This comment has been minimized.
|
If the |
pnkfelix
reviewed
Jun 28, 2016
|
|
||
| ## Implementation | ||
|
|
||
| The generated closure type will have generic implementations of `Fn`, `FnMut` and `FnOnce` with the provided type bounds. This is similar to the way closures currently have generic implementations over lifetimes. |
This comment has been minimized.
This comment has been minimized.
pnkfelix
Jun 28, 2016
•
Member
The way that closures are generic over lifetimes corresponds to types of the form for <'a> Fn(&'a u8) (for example), where the client code can instantiate that type at different lifetimes for 'a.
Is that analogy what you intend to apply here, keeping in mind that the above lifetimes are erased at compile time?
I ask because when I first saw this proposal, I had assumed that part of the intention was to enable one to write rank-N higher-order functions (think forall in Haskell), where a function passed as an argument is able to be instantiated at multiple concrete types.
A concrete example of what I mean:
fn hof_example<F>(f: F) -> i32
where F: for <T: fmt::Debug> Fn(T) -> i32
{
f(0.0) + f(true)
}Note: the above is certainly doable under a monomorphization strategy.
- It becomes ... harder when one tries to generalize it to object-types (
f: Box<for <T: fmt::Debug> Fn(T)>).
I'm just trying to understand the scope of what you're proposing.
In other words:
Can you speak more on the actual type assigned to these generic closures, or at least about the generic implementations here?
I.e. what type are you assigning to the expression
<T: Debug> |x: T| { println!("{:?}, x); }and what impls are generated for it?
I'm trying to figure out if this ends up being something like:
impl<T: Debug> Fn(T) for [closure@pnk-example] { ... }or if there is something broader being implicitly proposed here.
This comment has been minimized.
This comment has been minimized.
eddyb
Jun 28, 2016
Member
The closure is as generic as the function that created it, the impl can be more generic (with the usual requirement that the additional parameters can be deduced from the trait parameter types).
This comment has been minimized.
This comment has been minimized.
pnkfelix
Jun 28, 2016
•
Member
(after reading the comment thread a bit, it seems like rank-N types are not what is being proposed here. I still think the RFC text must be clarified to make that more clear.)
This comment has been minimized.
This comment has been minimized.
reddraggone9
commented
Jul 21, 2016
|
WIth this proposal, would this be possible? |
This comment has been minimized.
This comment has been minimized.
|
@reddraggone9 AFAICT, yes it would. |
steveklabnik
referenced this pull request
Nov 30, 2016
Closed
Cannot state a infered lifetime in a closure #38081
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
I've been thinking about this RFC. Here are a few thoughts:
Despite the second point, I think overall we should do this, once/if we have HRTB of type arguments. It seems like a logical extension with that feature in place. |
This comment has been minimized.
This comment has been minimized.
We could start by just doing it for lifetimes. |
This comment has been minimized.
This comment has been minimized.
|
@canndrew we do allow the construction of such closures, but we don't have syntax for ascribing the lifetime parameter. let closure = |x: &u32| *x;
{
let x = 0;
closure(&x);
}
{
let y = 0;
closure(&y);
} |
This comment has been minimized.
This comment has been minimized.
That's what I mean though. I've needed that syntax before. |
This comment has been minimized.
This comment has been minimized.
|
I suggest we postpone this RFC until we're a good bit further along with the impending trait system revamp, which I think should make this kind of thing much easier to implement and reason about. @rfcbot fcp postpone |
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Jan 31, 2017
•
|
Team member @aturon has proposed to postpone this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
mikeyhew
referenced this pull request
Feb 12, 2017
Open
Can’t declare lifetime for closure that returns a reference #22340
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Feb 23, 2017
|
|
rfcbot
added
the
final-comment-period
label
Feb 23, 2017
This was referenced Feb 25, 2017
This comment has been minimized.
This comment has been minimized.
rfcbot
commented
Mar 5, 2017
|
The final comment period is now complete. |
This comment has been minimized.
This comment has been minimized.
|
The FCP has elapsed without any further comments. I'm going to go ahead and close as postponed, to be revisited when the new trait system implementation is in place. Thanks @Amanieu for the RFC! |
aturon
closed this
Mar 6, 2017
This comment has been minimized.
This comment has been minimized.
burdges
commented
Jun 10, 2017
|
Just ran into a situation that'd benefit form these. |
This comment has been minimized.
This comment has been minimized.
|
This should probably be labelled as "postponed" for reference. |
This comment has been minimized.
This comment has been minimized.
|
Oh, the suggestion in #1650 (comment) is actually now viable since universal_impl_trait has been implemented (can't compile yet) #![feature(universal_impl_trait)]
use std::fmt::Debug;
// fn f(x: impl Debug) {
// println!("{:?}", x);
// }
fn main() {
let f = |x: impl Debug| println!("{:?}", x); //"
// (currently an error)
//~^^ ERROR [E0562]: `impl Trait` not allowed outside of function and inherent method return types
f(1);
f(true);
f((4, 5, 6));
} |
Amanieu commentedJun 15, 2016
This RFC adds the ability to define generic closures:
Rendered