Skip to content

[RFC] Implement block expressions - alternative implementation #5407

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

Closed
wants to merge 1 commit into from

Conversation

iluuu1994
Copy link
Member

As promised here is the alternative implementation of #5403.

Just to reiterate, this PR is different in that it doesn't touch the statement list ('{' inner_statement_list '}') we already have. This makes things much easier but it does still require specifically allowing statement lists in the arrow function and match grammar, so it doesn't really help in that regard.

@nikic Do you agree this is the way to go?

@nikic
Copy link
Member

nikic commented Apr 20, 2020

Yes, I agree that this is the better approach. As you mention in #5403 (comment) we can't really get around treating match expressions and match statements slightly differently, if only due to the trailing semicolon.

I wonder whether function foo() { stmt; expr } should be legal. I guess initially "no", as this seems like a nasty coding style issue.

@iluuu1994
Copy link
Member Author

I wonder whether function foo() { stmt; expr } should be legal.

I would say no for the same reason and argue the same thing for arrow functions. fn() => { return ...; } makes sense but why allow two variants? Which makes this seriously questionable as a language wide feature. I'd prefer just implementing this for match arms.

@nikic
Copy link
Member

nikic commented Apr 20, 2020

I wonder whether function foo() { stmt; expr } should be legal.

I would say no for the same reason and argue the same thing for arrow functions. fn() => { return ...; } makes sense but why allow two variants? Which makes this seriously questionable as a language wide feature. I'd prefer just implementing this for match arms.

I think I agree. As match is the only syntax we'll be accepting that can be used in both statement and expression context, it makes sense to limit the use of blocks expressions to that context as well, at least initially.

@iluuu1994
Copy link
Member Author

it makes sense to limit the use of blocks expressions

I think this is the best approach. I'll adjust this in the RFC and implementation. Thanks for your assessment!

I have one more question: What do you think about omitting the match statement semicolon?

match ($x) {
    ...
} // No semicolon necessary

It does make the grammar pretty complicated (same issue as with the first block PR). Additionally there's an ambiguity when nesting matches:

$x = match ($y) {
    default => {
        // Is this a statement or expression?
        match ($z) { ... }
    }.
};

With the current implementation the inner match would be parsed as part of the statement list which would trigger a compilation error (missing return value). I can't think of a good solution for this.

On the other hand I think requiring the semicolon would be incredibly irritating.

@TysonAndre
Copy link
Contributor

So far, there're some problems with goto and break inside of match expressions where the return value is used - #5371 (comment)

Supporting the general case of statement expressions would require solving the same issues (or almost the same) as match(true) { default => { stmts; expr} }

@iluuu1994
Copy link
Member Author

I'm closing this PR as we decided to go a different route. But we'll definitely have to fix break and goto for the match arm block anyway.

@iluuu1994 iluuu1994 closed this Apr 21, 2020
@iluuu1994
Copy link
Member Author

What do you guys think about this syntax:

match ($x) {
    default => {
        echo 'Foo';
        echo 'Bar';
        <= 'Baz';
    }
}

<= is only valid at the end of the block. It would fix the semicolon ambiguity and make returning the value much more obvious.

@TysonAndre
Copy link
Contributor

TysonAndre commented Apr 21, 2020

There's also => 'Baz' or = 'Baz', or even ==>, or a brand new keyword (e.g. ruby uses break, which makes no sense here). If a new keyword was introduced, I'd want something that made sense as a statement in if () {} or loops for potential future work.

  • I guess match ($x) { default => match(...) { ... }} <= 'Baz'; isn't a problem, because this was already forbidden from being the left hand side of a statement expression

The other things I can think of would conflict with other names resolve() makes more sense for promises and conflicts, endmatch or blockreturn are terrible choices for names

Right now, the only ambiguous case of having a semicolon is match(){}, so it seems to early to add more syntax. (match() { ... }) is fine with me

@iluuu1994
Copy link
Member Author

There's also => 'Baz' or = 'Baz', or even ==>

<= looks quite intuitive to me as it gives a sense of returning something to the parent.

or a brand new keyword

I thought about pass.

so it seems to early to add more syntax

Since { stmt_list; expr } is already new syntax it's probably better to think about it now.

TysonAndre pushed a commit to TysonAndre/php-src that referenced this pull request Aug 8, 2020
Conflicts:
	Zend/zend_compile.c
	Zend/zend_language_parser.y

Cherry-picked from php#5407 (written by Ilija Tovilo)
and patched by Tyson

This also changes the block expression syntax to ({})
outside of arrow functions for the convenience of getting
the last "expression" in a REPL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants