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

Function Structs #2276

Closed
wants to merge 1 commit into
base: master
from

Conversation

Projects
None yet
@porky11

porky11 commented Jan 4, 2018

This introduces function structs, which means, that for functions a new struct automatically gets implementet, which can be used as a function, which can be used to simulate keyword arguments and other useful things when calling functions.

Rendered

Function Structs
This introduces function structs, which means, that for functions a new struct automatically gets implementet, which can be used as a function, which can be used to simulate keyword arguments and other useful things when calling functions.

@Centril Centril added the T-lang label Jan 4, 2018

@brendanzab

This comment has been minimized.

Show comment
Hide comment
@brendanzab

brendanzab Jan 4, 2018

Member

Wouldn't this be supported once the Fn, FnMut, and FnOnce traits are stabilised?

Member

brendanzab commented Jan 4, 2018

Wouldn't this be supported once the Fn, FnMut, and FnOnce traits are stabilised?

@porky11

This comment has been minimized.

Show comment
Hide comment
@porky11

porky11 Jan 4, 2018

The fn-traits would just allow to use the operator (…) instead of .apply(…).
You still would have to implement the function as a struct and cannot use all functions as structs.
It may also be useful to implement fn-traits for the function structs automatically instead of apply(self).

porky11 commented Jan 4, 2018

The fn-traits would just allow to use the operator (…) instead of .apply(…).
You still would have to implement the function as a struct and cannot use all functions as structs.
It may also be useful to implement fn-traits for the function structs automatically instead of apply(self).

@cramertj

This comment has been minimized.

Show comment
Hide comment
@cramertj

cramertj Jan 4, 2018

Member

Possibly relevant: in the past @eddyb and I had discussed "brace calls" as a possible solution to making unapplied structs into first-class values, similar to how unapplied tuple-structs can be used as functions:

struct Foo(i32);
struct Bar { x: i32 }

let _ = Foo; // OK
let _ = Bar; // Currently an error

// But if we support this:
fn bar { x: i32 } { println!("{}", x); }
bar { x: 5 }; // Prints "5"

// Then Bar is just a function:
let _: fn{x: i32} = Bar;

One significant downside is that people don't expect brace-syntax to have side-effects, and so you'd be breaking users' intuition. It's definitely an interesting design space, though.

Member

cramertj commented Jan 4, 2018

Possibly relevant: in the past @eddyb and I had discussed "brace calls" as a possible solution to making unapplied structs into first-class values, similar to how unapplied tuple-structs can be used as functions:

struct Foo(i32);
struct Bar { x: i32 }

let _ = Foo; // OK
let _ = Bar; // Currently an error

// But if we support this:
fn bar { x: i32 } { println!("{}", x); }
bar { x: 5 }; // Prints "5"

// Then Bar is just a function:
let _: fn{x: i32} = Bar;

One significant downside is that people don't expect brace-syntax to have side-effects, and so you'd be breaking users' intuition. It's definitely an interesting design space, though.

@glaebhoerl

This comment has been minimized.

Show comment
Hide comment
@glaebhoerl

glaebhoerl Jan 4, 2018

Contributor

This is intriguing. Is there any reason we couldn't in principle (or do we already?) support #[derive] on fn items? Then this could just be provided on an opt-in basis as a procedural derive macro (e.g. #[derive(keyword_struct)] fn test(a: A, b: B) -> C), maybe even in a separate crate.

(🚲 I think .invoke() or .call() would be a better name than .apply(); maybe it should in fact just be an implementation of FnOnce, but test { a: x, b: y }() seems a bit too weird and cryptic.)

Contributor

glaebhoerl commented Jan 4, 2018

This is intriguing. Is there any reason we couldn't in principle (or do we already?) support #[derive] on fn items? Then this could just be provided on an opt-in basis as a procedural derive macro (e.g. #[derive(keyword_struct)] fn test(a: A, b: B) -> C), maybe even in a separate crate.

(🚲 I think .invoke() or .call() would be a better name than .apply(); maybe it should in fact just be an implementation of FnOnce, but test { a: x, b: y }() seems a bit too weird and cryptic.)

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Jan 4, 2018

Contributor

@glaebhoerl That sounds interesting. I think that the attribute #[derive(..)] is a bit overloaded already and we should be careful with overloading it more. So I'd prefer a different attribute name. For me, #[derive(..)] should always generate an impl for a trait, which stopped being the case with custom derive macros - this is an unfortunate exception, but an exception nonetheless.

Contributor

Centril commented Jan 4, 2018

@glaebhoerl That sounds interesting. I think that the attribute #[derive(..)] is a bit overloaded already and we should be careful with overloading it more. So I'd prefer a different attribute name. For me, #[derive(..)] should always generate an impl for a trait, which stopped being the case with custom derive macros - this is an unfortunate exception, but an exception nonetheless.

@sfackler

This comment has been minimized.

Show comment
Hide comment
@sfackler

sfackler Jan 4, 2018

Member

What struct would be generated for this signature?

fn foo(Bar { a, b: Baz(ref mut c) }: Bar) {
    ...
}
Member

sfackler commented Jan 4, 2018

What struct would be generated for this signature?

fn foo(Bar { a, b: Baz(ref mut c) }: Bar) {
    ...
}
@porky11

This comment has been minimized.

Show comment
Hide comment
@porky11

porky11 Jan 4, 2018

I had something like derive in mind when talking about macros for implementing it. But I don't think it's a good approach, because the creator of a function would need to implement it, and it's often not possible to use it.
This way the user could decide in any case to use it.

@sfackler
I didn't know something like that is possible.
If this is just a short way for destructuring values in the declaration instead of in the body, it shouldn't change the types of the struct, but the names.
In the simplest case a function struct would not get implemented by default in such cases.
I'll look at the exact meaning of this, there should be a better solution.

porky11 commented Jan 4, 2018

I had something like derive in mind when talking about macros for implementing it. But I don't think it's a good approach, because the creator of a function would need to implement it, and it's often not possible to use it.
This way the user could decide in any case to use it.

@sfackler
I didn't know something like that is possible.
If this is just a short way for destructuring values in the declaration instead of in the body, it shouldn't change the types of the struct, but the names.
In the simplest case a function struct would not get implemented by default in such cases.
I'll look at the exact meaning of this, there should be a better solution.

@burdges

This comment has been minimized.

Show comment
Hide comment
@burdges

burdges Jan 5, 2018

I know previous discussion for naming return types focused on fn_name::Output, so maybe fn_name::Args instead of overloading fn_name directly, except intuitively that's a tuple.

At first blush, I'd expect anonymous structs aka structural records do named arguments better, ala fn foo<T>(i: T, { x: T, y: T, z: Y}), and bring more to the language otherwise.

burdges commented Jan 5, 2018

I know previous discussion for naming return types focused on fn_name::Output, so maybe fn_name::Args instead of overloading fn_name directly, except intuitively that's a tuple.

At first blush, I'd expect anonymous structs aka structural records do named arguments better, ala fn foo<T>(i: T, { x: T, y: T, z: Y}), and bring more to the language otherwise.

@mark-i-m

This comment has been minimized.

Show comment
Hide comment
@mark-i-m

mark-i-m Jan 6, 2018

Contributor

I think the RFC doesn't really motivate the feature well... @porky11 could you give examples of why this is a useful feature? What does it improve?

Also, why can't this be implemented as a separate third-party crate with a macro-derive? Maybe that would be a better way to prototype it? I think that's what you meant when you said this:

But I don't think it's a good approach, because the creator of a function would need to implement it, and it's often not possible to use it.

I'm not sure understood what you mean...

Contributor

mark-i-m commented Jan 6, 2018

I think the RFC doesn't really motivate the feature well... @porky11 could you give examples of why this is a useful feature? What does it improve?

Also, why can't this be implemented as a separate third-party crate with a macro-derive? Maybe that would be a better way to prototype it? I think that's what you meant when you said this:

But I don't think it's a good approach, because the creator of a function would need to implement it, and it's often not possible to use it.

I'm not sure understood what you mean...

@ExpHP

This comment has been minimized.

Show comment
Hide comment
@ExpHP

ExpHP Jan 7, 2018

Note that the status quo in Rust for dealing with the keywords argument problem is to use the builder pattern, for which at least one proc macro derive crate already exists.

ExpHP commented Jan 7, 2018

Note that the status quo in Rust for dealing with the keywords argument problem is to use the builder pattern, for which at least one proc macro derive crate already exists.

@Havvy

This comment has been minimized.

Show comment
Hide comment
@Havvy

Havvy Jan 10, 2018

Contributor

On the reference level description, saying that a procedural macro can do it doesn't help. Using macros as a way of describing how something works should only be done for syntactic sugar. State something like "A function item defines both an item and a struct of the same name. The struct has XYZ fields and implements XYZ traits."


Also, how does this affect associated functions? Right now it's not possible to define new structs, enums, unions, and traits in an implementation, but if associated functions define new structs, then implementations do as well.

Contributor

Havvy commented Jan 10, 2018

On the reference level description, saying that a procedural macro can do it doesn't help. Using macros as a way of describing how something works should only be done for syntactic sugar. State something like "A function item defines both an item and a struct of the same name. The struct has XYZ fields and implements XYZ traits."


Also, how does this affect associated functions? Right now it's not possible to define new structs, enums, unions, and traits in an implementation, but if associated functions define new structs, then implementations do as well.

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Mar 1, 2018

Contributor

@rfcbot fcp postpone

This is a sort of interested idea, but I think the RFC is clearly not "completely baked" yet. I already feel like Rust's story around "tuples and braces", particularly when it comes to structs, is fairly complex -- this RFC seems to be adding another twist to that story, by making functions into structs. There are clearly some backwards compatibility concerns here (particularly around the issue of two namespaces), and also it doesn't seem to address any particular use case very well. That is, we don't get ergonomic optional arguments etc. Therefore, I think we ought to postpone this and come back and address ergonomic, first-class optional arguments at some later date.

Contributor

nikomatsakis commented Mar 1, 2018

@rfcbot fcp postpone

This is a sort of interested idea, but I think the RFC is clearly not "completely baked" yet. I already feel like Rust's story around "tuples and braces", particularly when it comes to structs, is fairly complex -- this RFC seems to be adding another twist to that story, by making functions into structs. There are clearly some backwards compatibility concerns here (particularly around the issue of two namespaces), and also it doesn't seem to address any particular use case very well. That is, we don't get ergonomic optional arguments etc. Therefore, I think we ought to postpone this and come back and address ergonomic, first-class optional arguments at some later date.

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Mar 1, 2018

Team member @nikomatsakis has proposed to postpone this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), 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.

rfcbot commented Mar 1, 2018

Team member @nikomatsakis has proposed to postpone this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once a majority of reviewers approve (and none object), 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.

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Mar 2, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

rfcbot commented Mar 2, 2018

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot

This comment has been minimized.

Show comment
Hide comment
@rfcbot

rfcbot Mar 12, 2018

The final comment period is now complete.

rfcbot commented Mar 12, 2018

The final comment period is now complete.

@Centril Centril added the postponed label Mar 12, 2018

@Centril

This comment has been minimized.

Show comment
Hide comment
@Centril

Centril Mar 12, 2018

Contributor

Postponing now that the FCP is complete.

Contributor

Centril commented Mar 12, 2018

Postponing now that the FCP is complete.

@Centril Centril closed this Mar 12, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment