Does anyone else find themselves wanting a shortha...
# announcements
d
Does anyone else find themselves wanting a shorthand operator to discard a result? Sometimes a function returns a result but you want it to parse/behave as though it's
Unit
. Any tricks to achieving this neatly?
s
Other than putting unit in the last line ?:
Copy code
{
  ...
  nonUnitExpression()
  Unit
}
f
One of those things that are nicely solved in Rust. I think we could extend Kotlin and enable
;
to do exactly this.
Copy code
{ nonUnitExpression(); }
Another use case that I would like to cover is https://en.wikipedia.org/wiki/Method_cascading
Copy code
obj.unitExpression()..nextFunOnObj()
e
But why would you need it in a language with automated memory management? 🤔
f
Not sure how your question relates to the request. There are situations in which you have a block that is supposed to not return anything but for some reason your last expression is. Having a shorthand to drop that result would be nice. This is the same in Rust (which also has automatic memory management, just not with a garbage collector).
e
Now I’m getting the context, but still do not exactly understand the problem. Kotlin compiler performs an automated “Unit coercion” to ensure that you’ll never have to worry about it yourself, so in that in a unit-returning lambda you can have the last statement with some result type and it just works without you having to do anything. @*darkmoon_uk* what’s the actual problem you are facing? Can you give an example piece of code?
p
You can end an expression with
.let{}
to return
Unit
but it's likely to confuse to the reader.
f
So one situation that I often have are functions that are supposed to return
Unit
and I would like them to be as terse as the others when implementing interfaces. This is not possible…
Copy code
fun f(): Unit = if (condition) doSomethingThatReturns() else doSomethingElseThatReturns()
In this case the only solution is to have:
Copy code
fun f() {
    if (condition) doSomethingThatReturns() else doSomethingElseThatReturns()
}
This can disturb the flow and make it much more verbose than it has to be.
e
You can do
fun f(): Unit = run { … }
But I, personally, as a matter of style, prefer to write
fun f() { return … }
in this case. What would you suggest to make it less verbose?
f
Basically what Rust does
;
. It’s semantics are clear imho and it yields the exact desired result. 🙂
fun f() = …;
instead of
fun f() { … }
or
fun f(): Unit = run {}
(which is the worst).
Another thing that makes
fun f() { … }
problematic is the fact that the default IntelliJ configuration is to insert an empty line before such a function and reformat the body. Hence, you cannot have …
Copy code
fun a() = expr
fun b() { expr }
… because it will become …
Copy code
fun a() = expr

fun b() {
    expr
}
… unless you fiddle with the settings.
Of course it should be mentioned that Rust always requires a
;
after each line unless you want to return in which case you can leave it off. The semantics in Rust are much more clearer than they would be in Kotlin. However,
;
is the (invisible) line terminator and using it as proposed is imho not too hard to understand. Of course I see that it might be confusing to some who are used to Java because
= expr;
is the same as
return expr;
for them. Maybe my Rust background is a bad influence here. 😄
p
You could overload an operator and use that
Copy code
inline operator fun Unit.rem(ignored: Any) = Unit
fun f() = Unit % if (condition) doSomethingThatReturns() else doSomethingElseThatReturns()
Can't say I like it though
or
Copy code
operator fun Unit.invoke(any: Any) = Unit
fun f() = Unit(if (condition) doSomethingThatReturns() else doSomethingElseThatReturns())
f
This is what I do for
runBlocking
in Junit so that I can to
@Test fun f() = runBlockingTest {
but it's too custom for anything else. I usually just live with the fact that it's not possible and tell my inner perfectionist that nobody else has these problems. 😄
p
Personally, I feel that
fun f() { ... }
is preferable here, and that single line function bodies shouldn't have the same rule applied for prefixing with an empty newline.
f
That would make various inline objects more verbose than they have to be.
p
Ignoring whitespace, it's an extra character over the
;
proposal, but with well known and understandable intent - I'm not sure how it's more verbose
in fact - it's the same:
=
->
{
,
;
->
}
f
For me the situation here is about readability and default settings. Having anything that looks like
{ … }
usually indicates a lambda, hence, the default formatting IntelliJ applies makes sense. However, using
;
means end of stmt another follows and is already established (e.g.
.map { expr; expr; expr; returnExpr }
. Hence, having
fun f() { … }
will trigger the lambda though whereas
fun f() = …;
triggers the end of stmt another follows thought. Well, at least for me. 😉
fun f() = { … }
vs
fun f() { … }
😕
p
I read expression-body functions as "the result of this function is this expression" - whereas
{ ... }
defines a block, or possibly a lambda depending on context. I would therefore read your example
fun f() = ...;
to be no different than without the
;
, in that the function should return the value of the expression.
f
See what I wrote above, I can see how you can come to that conclusion. For me it's natural (and in fact I even tried that the first time I encountered this issue) but it's all about habits, knowledge, expectations, …
d
I think writing
Unit
at the end of an expression lambda is the neatest solution in most cases. This does imply one is writing the lamdba for its side effects only, which is a code smell, but can be impractical to avoid when porting legacy code base from Java to Kotlin as I'm doing now.
For other syntactical scenarios; an empty extension function on
Any
to
.unit()
a result might be neatest? i.e.
fun Any.unit() = Unit
f
Copy code
class AutocompleteScope {
    private val buf = StringBuilder()

    infix fun <http://String.to|String.to>(value: Any) {
        buf.apply {
            append('\'')
            append(this)
            append("':'")
            append(value)
            append("',")
        }
    }
}
Would look much nicer without the extra
{}
😎
Copy code
class AutocompleteScope {
    private val buf = StringBuilder()

    infix fun <http://String.to|String.to>(value: Any) buf.apply {
        append('\'')
        append(this)
        append("':'")
        append(value)
        append("',")
    }
}
In this case I’m not using
=
nor
;
since this would already solve my use cases.