Really silly question, based completely on prefere...
# getting-started
a
Really silly question, based completely on preference πŸ™‚ I like to use
takeIf
a lot, and yes, there are other ways to do the below, but please bear with me πŸ™‚
Copy code
val printlnFn = println("hey")
printlnFn.takeIf { false }?.let { println("true") } ?: println("false")
The output of this will be
hey false
, and that is fair, but is there a lazy version of
takeIf
(no there is not, but any recommendation will be helpful) that will allow to call the fn only if the predicate of
takeIf
satifies?
Copy code
val printlnFn = { println("hey") }
printlnFn.takeIf { false }?.invoke() ?: println("false")
could work, but still I wonder if there's better πŸ™‚
r
I think I'd need a more realistic example of what it is you're trying to achieve to be able to suggest anything
βž• 2
a
sure, let's say you have a method, that makes a very expensive call, and in production you want to call it, but in test you don't want (because reasons). You can do a very simple
if/else
, you can do a
when
and this is fine, but if you use
takeIf
on the method, the method will be called and then either discarded or not according to the predicate within
takeIf
, I was just wondering if people have ever had a need for a lazy
takeIf
Clearer example:
Copy code
val perEnvCondition = false
fun veryExpensiveCall() = println("I spend a long time")
veryExpensiveCall().takeIf { perEnvCondition } ?: println("I do nothing")
r
The bit I'm not understanding is why takeIf would ever be preferable to an if statement or a when statement
a
it wouldn't πŸ™‚ it's just preference (I tried to highlight in the main thread that this is completely silly and based on me being stubborn with APIs)
r
Untitled.cpp
But it looks like something that's just going to confuse people to me
βž• 3
h
If you only want to run side effects, you can do something like: debug { myFunc() }
βž• 1
But for results or more complex calls, I would really recommend if/when. Don't fight the language
a
thanks, that's nice πŸ™‚ yeah, no doubt this is controversial, I just wanted to know if there was a kotlin API to do this
c
My personal preference: only use
.takeIf
if you want to reduce the scope of acceptable values from the previous call. For example,
Copy code
someString.indexOf('c').takeIf { it >= 0 }
is ok because it exists to remove negative integers from the domain space. In all other situations, I avoid
takeIf
& similar. (notice that all examples in the documentation follow this pattern)
πŸ’― 1
h
BTW the invokeIf from Rob will not work for a function that returns a nullable value, or more specific, the tenary operator ?: should not be used for this kind of code.
r
Fair, I've edited it to make it safe
h
Yeah, but the usage of the teary operator won't work if the function needs to return a nullable value.
r
Yup, but at least you won't accidentally call both. But yes, this is obviously safer and easier to understand:
Copy code
if (perEnvCondition) {
    veryExpensiveCall()
  } else {
    println("I do nothing")
  }
βž• 2
c
Yeah. These are all situations where a simple
Copy code
const val perEnvCondition = true

inline fun debug(block: () -> Unit) {
    if (perEnvCondition)
        block()
}
is much simpler. Usage:
Copy code
fun main() {
    debug { veryExpensiveCall() }
    // …
}
This kind of utilities is idiomatic and is common in logging libraries.
a
I did not want to start a very big debate πŸ™‚ I was just curious to see other people experience and if this was ever something that someone else would have ever wanted
I will stick with better operators in non side projects πŸ™‚
y
There is a version of this that I've seen people use (if memory serves, even in Kotlin compiler code):
letIf
, which is a conditional
let
For your case, I'd define an
onlyIf
like so:
Copy code
inline fun <T: Any> letIf(condition: Boolean, block: () -> T): T? {
  contract {
    condition holdsIn block
    returnsNotNull() implies condition
    returns(null) implies !condition
    // I have a gripe with this new contract, because it shouldn't be necessary due to contrapositives...
    condition implies returnsNotNull()
  }
  return if (condition) block() else null
}
// Usage
onlyIf(perEnvCondition) { veryExpensiveCall() } ?: println("I do nothing")
nice spin 1
o
Late to the party, but what's the point using this?
Copy code
val printlnFn = println("hey")
Unlike the name suggests, the type of
printlnFn
is
Unit
.
a
I did not want to write the full fn, in the second example I tried to make it clearer what this hypothetical method should do
o
But didn't you really mean to write it like so?
Copy code
val printlnFn = { println("hey") }
a
No, I have a method that does something, what the method returns is less important for the use case (assume some heavy op) but the method is not a supplier
o
So then with proper naming like so?
Copy code
val printlnResult = println("hey")
a
I tried to incline include the word
fn
in the name to make it clear that my method is actually a function rather than a side effect generator, but yeah
πŸ‘ 1