It would be very useful to have in Kotlin standard...
# language-evolution
a
It would be very useful to have in Kotlin standard library a way to print a message if a result of a computation is
null
without any additional burden, smth. like
tapNone
in arrow. Now I have to do the following:
Copy code
someComplicatedComputation()
        ?: run {
            println("We got null")
            null
        }
while for Option type in arrow I have much simpler and more readable:
Copy code
someComplicatedComputation()
        .tapNone {
            println("We got null")
        }
or did I overlook something from the standard lib?
j
Why not simply:
Copy code
val result = someComplicatedComputation()
if (result == null) {
    println("we got null")
}
It is as few lines as any of your options, and without any special operator or function.
a
Well, it doesn't work for most of the functions that I have, which are rather small and single-expression, e.g.:
Copy code
fun calculateIt() =
   someComplicatedComputation()
In order to apply your method I would need to change the form of all those functions to:
Copy code
fun calculateIt() {
   val result = someComplicatedComputation()
   return result
}
which is not what I am looking for.
j
What's the point in your first example if it's just one function calling directly another function? Why define
calculateIt
in the first place? If
someComplicatedComputation
is meant to represent a chain of operations in real code (and not a function call), then injecting side effects in such chains is not very great looking either. It is nice (IMO) to see the imperative nature of the thing you're doing by separating the chain.
But even without this consideration, you could of course use
Copy code
.also {
    if (it == null) println("we got null")
}
a
What's the point in your first example if it's just one function calling directly another function? [...]
this is not not relevant for my question 😕 As it comes to
also
- yes, it could be the solution but rather cumbersome. I don't want to run this check for all values, I just want to execute smth. for
null
values. This is the reason why for nullable type we have
?:
operator (with your approach we could easily write at the end of each expression
if == null
) and constructs like
?.let ... ?: ...
or why you have in some libs
tapNone
methods (in Kotlin Arrow and in many other languages and libs), this is not my invention. I just don't understand why we don't have a standard method for such a basic and very useful construct. And if we don't have it in Kotlin, I suggest to add it. That's the reason why I wrote this post.
j
I don't want to run this check for all values, I just want to execute smth. for null values.
That doesn't really make sense. Even if a
tapNone
implementation was provided by the stdlib, it would have to check whether the value is null in order to decide whether to execute the block of code. So the
if
check does happen on all values either way.
> or why you have in some libs tapNone methods (in Kotlin Arrow and in many other languages and libs) Arrow doesn't follow and support the same programming style as the Kotlin language design. If you want a real functional programming experience, railway programming and the likes, then libraries like Arrow are the perfect fit for you. But the Kotlin standard library and the language's design are not driven by the same programming style, so it will not by default provide facilities that help doing things in a different style, unless a clear pattern emerges and some construct really is proven useful even if Kotlin's direct style.
☝️ 1
I just don't understand why we don't have a standard method for such a basic and very useful construct
I guess no compelling use case has ever been brought up. I for one never needed this. It would help to show a real-life example where you need this.
> As it comes to also - yes, it could be the solution but rather cumbersome Is it really much more cumbersome? Imagine you did have a stdlib function, which could be called
alsoIfNull
for consistency:
Copy code
somethingNullable().alsoIfNull { println("foo") }
somethingNullable().also { if (it == null) println("foo") }
There is a big loss of generality with
alsoIfNull
. Should we also add
alsoIfNotNull
? What about
alsoIfIsInstance<T>()
? I don't think saving a few characters justifies a new function over the general
also
, unless the specific case occurs very often, or we can get some type inference benefit from it.
> this is not not relevant for my question 😕 Well, you kinda make the point that you have "small and single-expression" functions in which you want to inject imperative code in a way that still looks like an expression, so I find this bit quite important actually
a
functions in which you want to inject imperative code in a way that still looks like an expression
😱 Sorry, I feel we are not moving forward with this discussion. It's not about functional programming, it's not about complicated use cases, and all that stuff that you write about.... I provided the simplest possible use case (similar to code in many code bases) and let you know that other languages/libraries implement straightforward solution for this case (action on null for nullable types without result modification), also there are many similar questions on internet (e.g. https://stackoverflow.com/questions/45800036/how-do-i-run-a-block-of-code-if-a-nullable-type-is-null). Kotlin std lib has so many useful functions and constructs, hundreds of them I will never use, because I haven't found any good use case during many years of programming in Kotlin, yet such a useful function is not there. (I'd argue it is useful not only because myself and many other devs think that, but otherwise it wouldn't exist in other langs and libs, not necessarily functional)
j
> Sorry, I feel we are not moving forward with this discussion Sad to hear that. Sorry, I'm genuinely trying to understand and answer your points. > I provided the simplest possible use case By use case, I meant a more specific real-life example in which such function would be used, and demonstrating the advantage it would have over existing constructs. Sometimes with theoretical examples, we can miss each other's points or lack important details. > I'd argue it is useful not only because myself and many other devs think that, but otherwise it wouldn't exist in other langs and libs, not necessarily functional I don't think this is a valid point in general, it depends on the programming style encouraged by the language. It's not necessarily about functional vs imperative. It can be things like generally favoring being explicit over being concise (even when considering 2 languages striving for both). This kind of subtle tradeoffs. It is similar to comparing languages like Swift using
await
to call async functions vs Kotlin calling suspend functions like regular functions without
await
keyword. This kind of properties of a language can drive what the stdlib really needs or not. What matters more though is if people needed this a lot, which you did mention. It's hard to tell how many of the questions you are referring to would require a new stdlib function as you are suggesting or would be fine with just
?:
,
also
, or regular
if
statements.
k
It's easy to write it yourself:
Copy code
// Usage: fun calculateIt() = someComplicatedComputation() ?: runAndReturnNull { ... }
public inline fun runAndReturnNull(block: () -> Any?): Nothing? {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return null
}
a
Just make an extension yourself. Why would you want a library for it?
c
Without talking about this specific case one of the advantages of kotlins stdlib is how expansive it is. Often if I need a function it's just there. Other languages are lacking in this regard and suffer as a result. "You can write it yourself" is always an option (and if the use case is specifically niche probably the only option) but misses the above point.