Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions text/0000-generic-closures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
- Feature Name: generic_closure
- Start Date: 2015-06-15
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)

# Summary
[summary]: #summary

This RFC adds the ability to define closures that are generic over types.

# Motivation
[motivation]: #motivation

Generic closures can be used to support compound operations on tuple types:

```rust
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
struct Tuple<A, B, C>(pub A, pub B, pub C);

impl<A, B, C> Tuple<A, B, C> {
fn map<A2, B2, C2, F>(self, mut f: F) -> Tuple<A2, B2, C2>
where F: FnMut(A) -> A2 + FnMut(B) -> B2 + FnMut(C) -> C2
{
Tuple(f(self.0), f(self.1), f(self.2))
}

fn fold<T, F>(self, val: T, mut f: F) -> T
where F: FnMut(T, A) -> T + FnMut(T, B) -> T + FnMut(T, C) -> T
{
let val = f(val, self.0);
let val = f(val, self.1);
let val = f(val, self.2);
val
}
}

let a = Tuple(1u8, 2u32, 3.5f32).map(<T: Into<f64>>|x: T| x.into() + 1.0);
assert_eq!(a, (2f64, 3f64, 4.5f64));

let b = Tuple(1u8, 2u32, 3.5f32).fold(10.0, <T: Into<f64>>|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);
Copy link
Member

Choose a reason for hiding this comment

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

Are these bounds required? Can they be inferred?

Copy link
Member Author

Choose a reason for hiding this comment

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

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

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
.


<T>|x: T| where T: Debug {
println!("{:?}", x);
}
```

When using the `where` syntax, the braces around the closure body are mandatory.

## 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.
Copy link
Contributor

@pnkfelix pnkfelix Jun 28, 2016

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.

Copy link
Member

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).

Copy link
Contributor

@pnkfelix pnkfelix Jun 28, 2016

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.)


# Drawbacks
[drawbacks]: #drawbacks
Copy link
Member

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.


None

# Alternatives
[alternatives]: #alternatives

None

# Unresolved questions
[unresolved]: #unresolved-questions

None