TwoClocks
02/21/2022, 11:07 PMmaybeNull() ?: return
but this isn't maybeNull() ?: { return }
Joffrey
02/21/2022, 11:10 PM{ ... }
sometimes delimit lambdas in Kotlin, not always code blocks). And this lambda doesn't allow non-local returnsJoffrey
02/21/2022, 11:11 PMmaybeNull() ?: run { return }
TwoClocks
02/21/2022, 11:13 PMJoffrey
02/21/2022, 11:13 PMfun main() {
val x = maybeNull() ?: run {
println("In the null branch!")
return
}
println("Got some non-null x: $x")
}
private fun maybeNull(): String? = null
https://pl.kotl.in/pGwEnQHEjJoffrey
02/21/2022, 11:15 PMwhy would the compiler think it's a lambda?Why would the compiler think it's a code block? There is no
if
or when
or similar keyword / syntax construct that warrants a code block here. Kotlin doesn't have standalone code blocks like Java AFAIK, so those are always lambdas, they don't need any captured variables or arguments.TwoClocks
02/21/2022, 11:19 PMJoffrey
02/21/2022, 11:29 PMif
, when
, init
, function bodies, etc. If you're not using such a syntax, Kotlin consistently interprets braces as lambdas AFAICT, so it's not too surprising once you forget about arbitrary code blocks in the middle of some code flow.
There is almost no use for code blocks outside syntax constructs, and every time I saw standalone code blocks in Java, it was either a mistake or a code smell that a piece of code really should have been extracted into a function.TwoClocks
02/21/2022, 11:35 PMCode blocks are completely different from lambdas, so there is no real point in comparing complexity hereIsn't that exactly when you want to compare complexity? When things are different? code blocks also control scope. So I think saying they are useless outside of syntax is a naive.
Joffrey
02/21/2022, 11:54 PMIsn't that exactly when you want to compare complexity? When things are different?If the things you compare don't serve the same purpose, you wouldn't use them interchangeably, so I'm not sure why you would want to compare their complexity. It's like saying a class is more complex than an
if
statement - I don't quite see the point.
In the context of our discussion, you mentioned I'd assume it would always try to generate the simplest code, until it finds out it can't. I don't understand how you would even define "simplest code" then here. The simplest code that does what? The simplest code among which options? The fact that you put "code block" or "lambda expression" in the list of options to compare is arbitrary, and the language design team decided there was no ambiguity here - it is just "lambda expression".Joffrey
02/21/2022, 11:58 PMcode blocks also control scope. So I think saying they are useless outside of syntax is a naive.I'm aware. It's exactly their usage as scope-only (outside of any other syntax construct) that I consider code smells. I have never seen a situation where a standalone code block used as scope could not instead be clearer by being extracted into a function or removed. But I'd be happy to discuss one if you have an example where you consider one useful
ephemient
02/22/2022, 1:19 AM{ }
for scoping in Kotlin (or Java) isn't super meaningful, as it doesn't let you re-bind variables and it doesn't give you RAII (note that JVM is allowed to garbage collect references even before the end of the block, if the reference will not be used later)TwoClocks
02/22/2022, 2:44 AMIf the things you compare don't serve the same purpose, you wouldn't use them interchangeablyWe're talking about an EXACT case where they can be used EXACTLY interchangeably.
TwoClocks
02/22/2022, 2:51 AMusingI don't think that's the point (at least not for me. To me the point isfor scoping in Kotlin (or Java) isn't super meaningful{ }
{}
have different meanings in extremely similar contexts (e.g. not being fn param, etc). Yet they mean two different things. That seems like a design flaw in the language. The usefulness of meaning A vs meaning B is irrelevant.ephemient
02/22/2022, 2:52 AM{}
always means lambda except when in specific control structures, I don't think it's confusing at allTwoClocks
02/22/2022, 2:52 AM:?
not a control structure?ephemient
02/22/2022, 2:52 AM?:
takes an expression on both sides, {}
as an expression can only be a lambdaTwoClocks
02/22/2022, 2:53 AMif( thing == null)
ephemient
02/22/2022, 2:54 AMrun { val tmp = maybeNull(); if (tmp != null) { tmp } else { { return } }
is the equivalent of what you wroteTwoClocks
02/22/2022, 2:56 AMTwoClocks
02/22/2022, 2:56 AMephemient
02/22/2022, 2:56 AMrun@{}
is a lambda, and { return }
is a lambdaephemient
02/22/2022, 2:57 AMTwoClocks
02/22/2022, 2:58 AMTwoClocks
02/22/2022, 2:59 AM{ return }
yield?ephemient
02/22/2022, 3:01 AM{ return }
is a () -> Nothing
(or possibly a (T) -> Nothing
, T.() -> Nothing
, or T.(U) -> Nothing
depending on context)Joffrey
02/22/2022, 3:01 AMWe're talking about an EXACT case where they can be used EXACTLY interchangeablyNot at all. That's exactly why I initially wrote that they are different, because I was expecting that maybe you had misunderstood this part. The lambda would NOT be executed immediately in this case, it would just be a value of a function type. For instance:
val x = null ?: { println("test") }
In this case the value of x
would be a function that prints "test"
when invoked (x
would be of type () -> Unit
). But running this code would NOT print anything. If that were a regular code block, it would print.ephemient
02/22/2022, 3:06 AM{ return }
involves a non-local return, which can only be performed from an inlined lambda, which does not match the context in your example. but that comes up after the grammar has already decided that it is a lambda.TwoClocks
02/22/2022, 3:09 AMdoes not match the context in your example. but that comes up after the grammar has already decidedI think that's the exact point I'm trying to make.
ephemient
02/22/2022, 3:10 AM{}
as an expression in Kotlin is a lambda, full stop. there are no scope-only blocks.TwoClocks
02/22/2022, 3:12 AM{}
is always a lambda, even in simple if/while statments?ephemient
02/22/2022, 3:12 AMephemient
02/22/2022, 3:13 AMif
and while
keywords is a controlStructureBody which is a block | statement, not an expressionephemient
02/22/2022, 3:14 AMTwoClocks
02/22/2022, 3:14 AMval x = if(true) {1} else {2}
that's not an expression?ephemient
02/22/2022, 3:15 AM{1}
is a block, which contains one statement, which contains one expression, which is an integer literalJoffrey
02/22/2022, 3:15 AMif
expression you would need 2 pairs of bracesTwoClocks
02/22/2022, 3:17 AMhe only possible meaning forso... this isn't true then...as an expression in Kotlin is a lambda, full stop{}
ephemient
02/22/2022, 3:17 AMTwoClocks
02/22/2022, 3:18 AMTwoClocks
02/22/2022, 3:20 AM{}
be lambdas sometimes, and blocks others, even in extreamlly simluar case like ?:
which is sold as a "short hand if statment`, is 100% good language design?ephemient
02/22/2022, 3:20 AMephemient
02/22/2022, 3:21 AM?:
as a "short hand if statement"? the kotlin documentation consistently describes it as an operatorTwoClocks
02/22/2022, 3:24 AMTwoClocks
02/22/2022, 3:24 AMInstead of writing the completeexpression, you can also express this with the Elvis operator `?:`:if
TwoClocks
02/22/2022, 3:26 AMgiven the limited number of symbols on our keyboards, yes.has nothing to do with keys. The language could say "any lambda that does not yield a value is demoted as a block" blamo. done.
TwoClocks
02/22/2022, 3:27 AMephemient
02/22/2022, 3:31 AMJoffrey
02/22/2022, 3:31 AMUnit
and Nothing
types. Every block could therefore be a lambda. Thus the content of the block cannot help determine what is or isn't a lambda.
However, limiting blocks to control flow syntax, and interpreting lambdas in every expression is really consistent, and honestly not that hard to graspephemient
02/22/2022, 3:32 AMephemient
02/22/2022, 3:33 AM{ return }
as an immediately evaluated expression, but results in many other ambiguities throughout other usesephemient
02/22/2022, 3:36 AM【】
always signifies blocks and 「」
always signifies lambdas. but those are not accessible on a typical Western keyboardephemient
02/22/2022, 3:40 AMTwoClocks
02/22/2022, 3:48 AM||
to denote a lmabda, as well as define params... which I think is much better. never liked that the params are defined inside the lambda in kotlin {key,value-> stuff}
as apposed to |key,value| {stuff}
which is far less ambiguous, and the exact same number of keysstrokes.ephemient
02/22/2022, 4:07 AMephemient
02/22/2022, 4:08 AM