-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Generic closures #1650
Generic closures #1650
Conversation
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 |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am suspicious of any proposal that has zero drawbacks, alternatives, and unanswered questions.
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 |
Where do we put the move <T: Debug> |x: T| ...
<T: Debug> move |x: T| ... |
I doubt the feasibility here is that generic functions can't be values. |
@F001 The closure only has a single type, it's just that it implements the |
Would this let you do: let foo = <T: Debug>|x: T| println!("{:?}", x);
foo(42);
foo("bar"); (I'm guessing no.) |
@durka Yes it would. |
I see. The generic type parameter can only used in the arguments, not the captured values. |
@kennytm The |
That's really neat. I'm fully in favor of this. 👍 |
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. |
@glaebhoerl does it conflict with potential HKT syntax? (or is this just HKT-for-closures as it is?) |
HKT is totally separate from this I think. It's "higher-ranked trait bounds quantified over types and not just lifetimes" (iow, " |
Right. So... if we end up using |
I don't think it would be confusing, because the meaning matches up. The expression |
We wouldn't. |
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. |
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 |
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
|
@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. |
|
+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. |
@canndrew In fact, we support that today, if the closure is an argument to a generic function which has a |
|
||
## 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(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.)
WIth this proposal, would this be possible? |
@reddraggone9 AFAICT, yes it would. |
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. |
We could start by just doing it for lifetimes. |
@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);
} |
That's what I mean though. I've needed that syntax before. |
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 |
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. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period is now complete. |
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! |
Just ran into a situation that'd benefit form these. |
This should probably be labelled as "postponed" for reference. |
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));
} |
This RFC adds the ability to define generic closures:
Rendered