-
Notifications
You must be signed in to change notification settings - Fork 4
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
[DRAFT] RFC: Function body shorthand syntax #17
base: master
Are you sure you want to change the base?
Conversation
Finally, a rather minor motivation, which still bears mentioning, is | ||
that by permitting `fn foo(...) -> R = expr;` as a syntax, we bring `const` | ||
and `static` items closer to `fn` items syntactically which gives a smoother | ||
experience overall. In other words, we can now write: |
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 disagree that this gives a smoother experience. I think this is confusing, as static, consts and fns work differently. Having equality makes it seem like functions should be eagerly evaluated.
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.
This is not really the place to debate the proposal itself but...
I think that's quite a stretch; in none of the languages noted in the prior art does such confusion occur as there's clearly an argument that needs to be provided to the function before anything happens, so how could it possibly be eagerly evaluated?
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.
Some of the languages I would argue are not analogous to Rust (e.g. Haskell / any lazy language), but regardless, a function with no arguments could plausibly be eagerly evaluated. In many of the languages, =
is the default way to specify a function, which I think makes it less confusing.
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.
- A function with no arguments is really a function that is
() -> ReturnType
so it still takes an argument. - In a function with no arguments you are also more likely to write
fn main() { ... }
because the purpose of such a function is primarily its side effects. - Also, most functional programming languages, e.g. ML, OCaml, Scala, and Idris are all strict but no such confusion occurs.
- Scala, Kotlin, and C# all support both
{ ... }
and=
(or=>
in C#) and there doesn't seem to arise such confusion from this. Notably the Scala users that use Rust seem to like this idea from what I can tell.
I think this is a bad idea. You add extra syntax to the language, making it harder to learn ("What's the difference between a function defined with This makes the language less consistent, not more consistent. I think we should be trying to address the confusing/inconsistent parts of the language first before trying to add syntactic sugar wherever possible. |
Again this is not really the place do discuss the proposal itself; only to make factual corrections, typo fixes, add more context, or other sort of fixes...
I don't think it makes it notably harder to learn; the meaning of the syntax is inferable from the syntax itself.
What is the difference if you write
To me
Rightward drift yes (which is not minimal imo), but also a) more familiarity for functional programmers, b) fully embracing the expression oriented feeling, c) encouraging smaller function definitions.
I again disagree due to the aforementioned improvements wrt. expression oriented nature as well as
I've written several such proposals that plug consistency holes and I can do both at the same time. :)
What are you referring to? In which way is this "wherever possible"? |
Okay, it'll be better to save my comments for when the RFC is posted, in that case. (Feel free to hide my posts as off-topic!) |
Well; feel free to continue the discussion here with me since we already started it... :) |
So, I'm torn. On the one hand, I can see some really compelling arguments for this, and I can easily imagine wanting to use it myself. On the other hand, I feel like this could easily let a braced construct get confused with function braces in a way that isn't possible today. I feel like this would not be intuitive for anyone not coming from a language that had it. I don't really want function syntax to look like a simple assignment. I do also feel like @varkor has a point. This is a fundamentally different Can we go back to the problem you're trying to solve, and consider other ways to solve it? |
Not sure why I was pinged, but I actually like this despite my general conservativeness towards extending the language! First, this is a very tiny and very surface change technically, not a full-blown new feature, but the effect is great. But primarily, I like using one-liners when content does fit into a single line, and saving vertical space in general (and hate For symmetry we can probably extend const/static items later to support braced initialization later const C: u8 {
/* long const-evaluation */
} This would also emphasize the fact that const/static initializers and fn bodies are objects of the same nature. |
On November 25, 2018 8:04:41 AM PST, Vadim Petrochenkov ***@***.***> wrote:
Second, this closes the door for more special-cased proposals like
`match fn` that I see from time to time.
This is the biggest thing appealing to me too.
|
The core goal is to make it really ergonomic to write short and descriptive function definitions (as opposed to long and deep / cyclomatically complex spaghetti) and to thus improve upon readability and in particular the maintainability of code bases.
I'm almost a bit embarrassed to be proposing a syntax thing which I would love to use myself so much instead of writing big and semantically significant proposals. =P
Could you elaborate? The reverse syntactic unification that @petrochenkov noted should make that quite difficult I think. I thought about proposing it in this RFC but ultimately I thought I'd do one controversial step at a time. ;)
Function definitions in the most basic mathematics classes are usually denoted with |
On Sun, Nov 25, 2018 at 08:44:41AM -0800, Mazdak Farrokhzad wrote:
@joshtriplett
> On the other hand, I feel like this could easily let a braced construct get confused with function braces in a way that isn't possible today.
Could you elaborate? The reverse syntactic unification that @petrochenkov noted should make that quite difficult I think. I thought about proposing it in this RFC but ultimately I thought I'd do one controversial step at a time. ;)
I'm talking about this:
```
fn long_function_name<T: SomeTrait>(some: Arguments, more: Args) -> Type = while something {
// long function body
// more long function body
// spanning more than a page of code
}
```
Does it not seem quite easy to miss the bit between the `=` and the `{`
and just see that as a function body?
I am *completely* in favor of short functions like
```
fn add_1(x: i32) -> i32 = x + 1;
```
That seems like a win in every way. My concern applies when the
expression contains braces.
I think my concerns might completely evaporate if we decided (via style
team and rustfmt) to format such things in a way that the difference
stands out, such as:
```
fn long_function_name<T: SomeTrait>(some: Arguments, more: Args) -> Type
= while something {
// long function body
// more long function body
// spanning more than a page of code
}
```
Or similar. Yes, I realize that removes a bit of the advantage of
reducing rightward drift, but frankly a function long enough that you
need to worry about rightward drift should perhaps not be using this in
the first place.
|
Finally got some time to get back to this... :P
Maybe... If I don't think this would be confusing for You could also say that the source of the problem is the long function body and if you write: fn long_function_name<T: SomeTrait>(some: Arguments, more: Args) -> Type {
while something {
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
}
} then it seems to me that it is just as easy to forget about fn long_function_name<T: SomeTrait>(some: Arguments, more: Args) -> Type {
let foo = {
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
// stuff
};
}
It seems to me that the consistent way to format fn long_function_name<T: SomeTrait>(some: Arguments, more: Args) -> TypeThatIsReallyTooDamnLong
= while something {
// long function body
// more long function body
// spanning more than a page of code
}; This is what happens with
There are plenty of functions which do or should immediately pub fn eval_expr(expr: &Expr) -> Option<u128> = match expr {
Expr::Lit(expr) => eval_lit(expr),
Expr::Binary(expr) => eval_binary(expr),
Expr::Unary(expr) => eval_unary(expr),
Expr::Paren(expr) => eval_expr(&expr.expr),
Expr::Group(expr) => eval_expr(&expr.expr),
_ => None,
}; To not reduce the advantage of this style (or |
On Fri, Dec 21, 2018 at 03:09:54PM -0800, Mazdak Farrokhzad wrote:
You could also say that the source of the problem is the long function body and if you write:
```rust
fn long_function_name<T: SomeTrait>(some: Arguments, more: Args) -> Type {
while something {
// stuff
// stuff
// stuff
// stuff
// [elided]
}
}
```
then it seems to me that it is just as easy to forget about `while something {`.
You can't forget that you're in *some* kind of block, though, because
you're indented two levels. That doesn't hold for this shorthand syntax.
> I think my concerns might completely evaporate if we decided (via style team and rustfmt) to format such things in a way that the difference stands out, such as:
[...]
> Or similar. Yes, I realize that removes a bit of the advantage of reducing rightward drift, but frankly a function long enough that you need to worry about rightward drift should perhaps not be using this in the first place.
There are plenty of functions which do or should immediately `match` or `if let` on their arguments and then they span a few lines (say <= 50, which fits within a single page). Those are functions are an important motivation for this RFC and I think it's desirable for them to be written as:
```rust
pub fn eval_expr(expr: &Expr) -> Option<u128> = match expr {
Expr::Lit(expr) => eval_lit(expr),
Expr::Binary(expr) => eval_binary(expr),
Expr::Unary(expr) => eval_unary(expr),
Expr::Paren(expr) => eval_expr(&expr.expr),
Expr::Group(expr) => eval_expr(&expr.expr),
_ => None,
};
```
I don't find that any more readable than it would be if indented one
more level.
Again, I find the use case compelling, just not the proposed syntax.
|
Rendered
This is a draft version of an RFC for you to review, before a formal proposal is made for consideration.
cc @nikomatsakis @alercah @eddyb @kennytm