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

Support non-local break and continue inside inline lambdas #326

Open
strangepleasures opened this issue Oct 31, 2022 · 7 comments
Open

Support non-local break and continue inside inline lambdas #326

strangepleasures opened this issue Oct 31, 2022 · 7 comments

Comments

@strangepleasures
Copy link
Contributor

strangepleasures commented Oct 31, 2022

This issue is for discussion of the proposal to support non-local break and continue inside inline lambdas in Kotlin.

Proposal PR #327.

@strangepleasures strangepleasures changed the title Support break and continue inside inline lambdas Support non-local break and continue inside inline lambdas Oct 31, 2022
@joffrey-bion
Copy link

FYI the proposal link is broken, maybe it should be updated to point to the KEEP in master

@joffrey-bion
Copy link

joffrey-bion commented Jan 31, 2023

I had missed this KEEP proposal. Sorry to bring an opinion this late. I guess there is no turning back from this at this point, but I just wanted to mention for the record that this feature encourages nested constructs.

Having no break in lambdas was a nice way to encourage developers to extract their loops into separate functions and use return instead. I already see production code with functions of hundreds of lines, with many loops and arbitrary mixed logic. This proposal will lift one barrier that prevented people from making a mess, and that's sad IMO.

Also, the return@label syntax is usually a red flag for me that something needed to be extracted into a function and should use a plain return. Now I'm afraid we'll see a proliferation of labeled breaks.

Of course this is all a matter of code style, but if the language can steer devs towards a better style, I think it should.

Now, I cannot make the same argument for continue. And if we allow this, then the argument of consistency gains much more weight. It would be strange to allow non-local return AND continue, but not break. So I sort of made my peace with this proposal. Actually, when I think of it, I think I have a problem with break as a general feature in the language, not just non-local breaks, so maybe this discussion doesn't have a place in this proposal.

@elizarov
Copy link
Contributor

joffrey-bion Thanks. I've fixed the link.

Having no break in lambdas was a nice way to encourage developers to extract their loops into separate functions and use return instead.

Not really. Most for the cases where I personally run into "WTF, I cannot use break here" were really short functions that would not benefit from splitting them into even more functions in any meaningful way.

@joffrey-bion
Copy link

Thanks! Do you have an example of such function? It might actually change my mind on this

@elizarov
Copy link
Contributor

elizarov commented Feb 2, 2023

I don't have an actual piece of code handy, but I think I know the usual pattern when it bites me personally. Often that's a typical use of continue when you have a precondition to process an element combined with scope functions. The easiest one is to continue when something is missing. Say, you are processing some complicated business object in a loop with a goal of sending notifications or whatever. It could start as simple as this:

for (user in users) {
    if (user.address == null) continue
    sendNotification(user.name, user.address, user.configuration, user.template)
}

Now you notice that you have user. repeated everywhere and you'd like to make this code cleaner using with scope function:

for (user in users) with(user) {
    if (address == null) continue // WTF: Does not compile anymore!
    sendNotification(name, address, configuration, template)
}

@joffrey-bion
Copy link

joffrey-bion commented Feb 2, 2023

My point was against break (not continue), because a loop with break can almost invariably be extracted into a function with a return instead (if your example was with break, we could change it to return and be done). Now again, I think my problem is with break in general, not just with break in inline lambdas, and the reason I felt uneasy about this proposal is that it just allows break in more places.

The use of continue is more defensible. I would usually prefer filtering using filter because I like how it expresses what I want, but that depends on whether want to use functional programming (and whether we can afford the extra list allocation). If we want to use loops, we probably need continue. And since Kotlin doesn't favor one paradigm over the other, it makes sense to have it.

The main argument for this proposal is consistency with allowing non-local returns, so I can't really argue against that. And as I said it would be weird to allow return and continue without also allowing break.

The problem with made-up examples is that we can find solutions that maybe in real life were not available. For instance, the fact that I pass 4 arguments that are all properties of user would not make me use with(user) but rather refactor the sendNotification function to take a user as input instead (or create an overload):

for (user in users) {
    if (user.address == null) continue
    sendNotificationTo(user)
}

And if an extra list is not a problem, it would probably look like this:

users.filter { it.address != null }.forEach { user -> sendNotificationTo(user) }

@elizarov
Copy link
Contributor

elizarov commented Feb 2, 2023

joffrey-bion The real life is complicated. Sometimes you can refactor, sometimes you cannot. It depends. Both break and continue are an established part of programmer's tool belt in virtually every modern language. I don't feel like discussing here in which cases break or continue should or should not be used. In the end, it all boils down to a particular problem at hand and a personal style preference, e.g. some people are willing to take extra time to figure out the way to rewrite their code in a purely functional style without using any breaks and continues, while others just use them without second thought and move along. This particular KEEP on support for non-local break and continue has nothing to do with it. It basically fixes an irregularity in the language design, where there are two separate features in the language that don't work together for quite an arbitrary compiler implementation reason. Here, adding support for continue but not adding support for break would be even worse -- it will result in a totally arbitrary rule that users will have to somehow remember, because unscrupulous interviewers will start using it as a tricky question to test one's knowledge of the Kotlin language.

We do have other arbitrary limitations where some features cannot be used together, which are driven by the complexity of the corresponding interactions. We are generally leaning towards removing these limitations, not adding more of them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants