Skip to content
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

Shorthand for fns that immediately match #1577

Open
nikomatsakis opened this Issue Apr 8, 2016 · 23 comments

Comments

Projects
None yet
@nikomatsakis
Copy link
Contributor

nikomatsakis commented Apr 8, 2016

There is a desire to have some sort of shorthand for functions that immediately match on their argument. Currently, one must write:

fn foo(x: Type) { match x { ... } }

or

|x| match x { ... }

It would be nice to be able to elide the match (and perhaps not even give a name to the parameter x). Many functional languages like Haskell and Scala have these sorts of shorthands.

Related RFCs:

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Aug 29, 2016

It does seem like a bit of a special case though... What if match was defined to be an inlined function call... That is match could be sugar for a function that does the match returns the result.

@Stebalien

This comment has been minimized.

Copy link
Contributor

Stebalien commented Aug 29, 2016

@mark-i-m You mean my_iterator.filter(match { 0 => true, _ => false }) (no closure)?

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Aug 29, 2016

Yes, or rather an implicit closure

On Aug 29, 2016 9:38 AM, "Steven Allen" notifications@github.com wrote:

@mark-i-m https://github.com/mark-i-m You mean my_iterator.filter(match
{ 0 => true, _ => false }) (no closure)?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#1577 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AIazwCmwL5q6s-80Au8MFe08W5CPXgbhks5qku7QgaJpZM4IDY2s
.

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Aug 29, 2016

Now, that I think about... I think we would have to clarify when match
would return the closure vs execute the closure, which could be inelegant.

On Aug 29, 2016 9:51 AM, "Mark Ishak Mansi" mmansi@utexas.edu wrote:

Yes, or rather an implicit closure

On Aug 29, 2016 9:38 AM, "Steven Allen" notifications@github.com wrote:

@mark-i-m https://github.com/mark-i-m You mean my_iterator.filter(match
{ 0 => true, _ => false }) (no closure)?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#1577 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AIazwCmwL5q6s-80Au8MFe08W5CPXgbhks5qku7QgaJpZM4IDY2s
.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Aug 31, 2016

just writing match { <arms> } would indeed work as syntactic sugar for |x| match x { <arms> }. I don't believe there is an ambiguity there, because we don't currently permit a block to appear as the match expression (I think). Whether we'd want to do it idk. I don't hate it, actually, but I might prefer to have a syntax where you can use _ as a placeholder, like scala, and hence you would write match _ { <arms> } in place of |x| match x { <arms> } -- but you could also write _ + 5 in place of |x| x + 5 and _ + _ in place of |x, y| x + y. The challenge here tends to be defining the extent of the closure, though, which is kind of ambiguous.

@netvl

This comment has been minimized.

Copy link

netvl commented Aug 31, 2016

but I might prefer to have a syntax where you can use _ as a placeholder, like scala

This thing would be simply wonderful! I'm writing Scala on my main job, and every time I return to writing Rust I miss the shorthand syntax for closures very badly.

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Aug 31, 2016

I am wondering if there would be any need/way to also have a shorthand for move |x| expr.

@nikomatsakis

This comment has been minimized.

Copy link
Contributor Author

nikomatsakis commented Sep 1, 2016

@mark-i-m yes, a good point. of course we could permit one to just use move, though it reads more awkwardly.

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Sep 1, 2016

Hmmm... yes, but it does seem to mar the usual elegance of rust syntax... I am beginning to become a bit opposed to the idea of adding special syntax...

@mdinger

This comment has been minimized.

Copy link
Contributor

mdinger commented Jan 8, 2017

#1612 is similar as are these two forum threads: older and the newer.

@burdges

This comment has been minimized.

Copy link

burdges commented Jan 8, 2017

At present a code block can appear as a match expression, like if you write a do .. while loop as while { body; condition } { }, but a match block with => would presumably be another animal entirely.

Rust has issues with currying, right? Just with kinds or even more basic? I've encountered reborrowing problems with doing say

let s : &`static str = match (x,y) { 
    (None,None) => ..,
    (Some(_),None) => ..,
    (None,Some(_)) => ..,
    (Some(_),Some(_)) => ..,
}

I donno if they could be fixed by trying harder though, but I doubt tuples work either. As a result, we might be stuck as one parameter for this. If so, then closure notations match _ { .. } alone might suffice, which makes everything easier.

If you do need notation for non-closures, then maybe

fn f(Ok(x): Result<X,Y>) => T { .. }
fn f(Err(y): Result<X,Y>) => T { .. }

but obviously this would benefit form the Haskell, etc. style detached function types.

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Jan 9, 2017

Frankly, I think if this sort of shorthand is to be done, it should be possible to promote any expression into a closure, not just match blocks. And to my knowledge this is possible with Scala's _ notation, right?

@tupshin

This comment has been minimized.

Copy link

tupshin commented Jan 22, 2017

+1 to borrowing from scala on this one

@Rufflewind

This comment has been minimized.

Copy link

Rufflewind commented Feb 13, 2017

It'd be more useful if it could be used for both closures and functions and for all possible arities, e.g.:

fn foo(match) {
    (Some(n)) => n,
    (None) => 0,
}

let bar = |match| {
    (Some(n), _) => n,
    (None, k) => k,
};

println!("{}, bar(None, 42));

That way, Rust would get not just a LambdaCase clone but also a more general analogue of Haskell's pattern matching in function definitions:

foo (Just n) = n
foo Nothing  = 0
@leonardo-m

This comment has been minimized.

Copy link

leonardo-m commented May 30, 2017

On the other hand it's also a good idea to avoid introducing too many special cases for a minor gain in succinctness... especially if this introduces pitfalls in the language.

@dobkeratops

This comment has been minimized.

Copy link

dobkeratops commented Aug 11, 2017

how about fn foo(...) = match { pat1=>expr1, pat2=>expr2, ... } ... sugar for single expression functions , dropping a nesting level ( and special cased for match) The other place this might be nice is constructors, e.g. fn make_bar(..) = Bar{ ... } kill two birds with one stone

@mark-i-m

This comment has been minimized.

Copy link
Contributor

mark-i-m commented Aug 12, 2017

@alercah

This comment has been minimized.

Copy link
Contributor

alercah commented Aug 26, 2018

Had a discussion with @Centril about this idea yesterday; I independently came up with @dobkeratops's idea.

My personal leaning here is two features:

  • Allow a function to have a body of an arbitrary expression, with = (or =>?) required as a disambiguator if it is not a block. This has plenty of precedent and I am a major fan of simplifying things, so that you can write e.g. fn square(x: i32) = x * x;. In my experience, this is huge for encouraging lots of small functions expressing e.g. predicates on types, since they become very cheap to write and take up very little space.

    EDIT: To expand a bit, I think that this has a nice benefit of basically making function definitions work similar to lambdas in that a block is optional and it's really just an arbitrary expression. Now that I think about it more, I'm leaning a bit towards => since it opens the door to if x => panic!("...") as a nice one-liner in a very uniform syntactical style, should we feel that's desirable.

  • Add syntax, either match { ... } or match _ { ... }, to automatically pull function parameters into match statements. Note that _ might be more extensible in that _ could be used to refer to "all parameters of the function" in expression context, if we wanted:

    struct Point {
        x: usize,
        y: usize,
     }
    
    impl Point {
        fn new(x: usize, y: usize) {
            if x > MAX || y > MAX { panic!("out of bounds!"); }
            Point{_}
        }
    }
@burdges

This comment has been minimized.

Copy link

burdges commented Aug 26, 2018

Would fn foo(..) = ..; infer the return type? Or do you mean more like

fn foo(x: X, y: Y) -> Z = Z::Initial(match x { ... }?);
@alercah

This comment has been minimized.

Copy link
Contributor

alercah commented Aug 26, 2018

I'm not strongly opposed to inferring the return type, but yes, I left it out. I think that, apart from sharing a goal of brevity, inferred return types are completely orthogonal as a feature.

@softprops

This comment has been minimized.

Copy link

softprops commented Nov 19, 2018

Just discovered this gh issue. Big fan of this in what ever form it may take. I transitioned into rust from scala and miss this very much.

foo(|x| match x { Pat(_) => ... })

has always felt more redundant than what the analog might look like from scala ( just using the body of the match block )

foo({ Pat(_) => ... })

@1011X

This comment has been minimized.

Copy link

1011X commented Jan 2, 2019

Although I didn't initially agree with the original post, after writing Rust for a while I can see why this would be convenient for some. There are some parts that get pretty redundant (especially when type names are involved), and the rightward drifting of code becomes too common.

In terms of syntactic style, something like this feels like it'd fit right in:

fn unwrap(opt: Option<i32>) -> match opt {
    Some(n) => n,
    None => panic!(),
}

No redundant braces, less indenting, and it's easy to read (for me at least).

Similarly, I've written code that looks like this:

fn new() -> Struct {
    Struct {
        field: 1,
        // more fields...
    }
}

which is a very common pattern, but for something that simple I wish I could write this instead:

fn new() -> Struct {
    field: 1,
    // more fields...
}

That said, something like this would require some combination of an optional/inferred return type for functions, and allowing any expression after an fn declaration (at least in that specific case where the return type is left inferred). Inferred return types I don't think would cause any major issues. The biggest problem I can see is the initial ambiguity between the latter 2 examples, and how it'd have to be handled.

An alternative, as @alercah mentioned, would be to use = or => instead of ->, which would solve the ambiguity, but I personally like -> more.

Possible odd cases:

struct S;      // unit struct
fn new() -> S; // valid, but is useless and looks like a constructor with no code
// ambiguous integer type unless suffixed or left for compiler to choose at the end.
// ...feature?
fn nice() -> 69;
@samuela

This comment has been minimized.

Copy link

samuela commented Mar 16, 2019

May be worth noting that OCaml and F# also have this shorthand: https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/match-expressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.