I have the following functions to simulate the ternary operator for kotlin
fun Boolean.then(action: () -> Unit): Boolean {
if (this)
action.invoke()
return this
}
fun Boolean.otherwise(action: () -> Unit) {
if (!this)
action.invoke()
}
fun <T> Boolean.then(func: () -> T): T? {
if (this)
return func.invoke()
return null
}
fun <T> T?.otherwise(action: () -> T): T {
return this ?: action.invoke()
}
they are supposed to be used like this :
(check).then { doHello() }.otherwise { doWorld() }
val answer = (check).then { "hello" }.otherwise { "world" }
however when I try to assign a value using the above operators like this:
val visibility: Int = (show).then { View.VISIBLE }.alt { View.GONE }
I get an error saying that the required reply was Int but it actually got Unit which means that it called the first version of the methods instead of the second
Other than renaming the methods (when I changed the first two to thenDo and otherwiseDo it worked), can I write the above code in some way so that the compiler will know to call the second version?
I don't think you need both overloads. If you remove the ones that return Unit, then both your lines of code work:
(check).then { doHello() }.otherwise { doWorld() }
val answer = (check).then { "hello" }.otherwise { "world" }
That's because the first line, where the lambdas return Unit, e.g. doHello(), can still use the generic versions of then and otherwise, as they are still considered functions with a return value, namely Unit.
Although I agree with some the comments above: do you really need this? Why not just use if, which is an expression which returns a value (like the ternary operator). See discussion here for more info.
Related
Let's say I have a simple class Foo with a nullable String?
data class Foo(
val bar: String?
)
and I create a simple function capitalize
fun captitalize(foo: Foo) = when {
foo.bar != null -> runCatching { foo.bar.capitalize() }
else -> ""
}
which works fine, because the compiler infers that foo.bar cannot be null eventhough it's type is nullable. But then I decide to write the same function as an extension of Foo
fun Foo.captitalize2() = when {
bar != null -> runCatching { bar.capitalize() }
else -> ""
}
and all of a sudden the compiler is no longer able to infer that bar is not null, and IntelliJ tells me that "only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable reciever of type String?"
Can anyone explain why?
I think it's because in the first case you are calling this function:
public inline fun <R> runCatching(block: () -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
but in the second case you are calling function with receiver:
public inline fun <T, R> T.runCatching(block: T.() -> R): Result<R> {
return try {
Result.success(block())
} catch (e: Throwable) {
Result.failure(e)
}
}
For me, it looks like an issue in the Kotlin compiler because if you inline code of this function by yourself it will work fine:
fun Foo.captitalize2() = when {
bar != null -> try {
Result.success(bar.capitalize())
} catch (e: Throwable) {
Result.failure<String>(e)
}
else -> ""
}
btw, if I were you I would like to write my capitalize2 function like this :)
fun Foo.captitalize2() = bar?.capitalize() ?: ""
So, finally I found an alternative approach that allows us to use runCatching without having the problem you shows.
As in my comment to the answer of #Andrei Tanana, in your code type parameters of fun <T, R> T.runCatching(block: () -> R) : Result<R> are inferred as <Foo, String> and the compiler can't use the information that this.bar is not null.
If you rewrite the capitalize2 function as follows
fun Foo.capitalize2(): Serializable = when {
bar != null -> bar.runCatching { capitalize() }
else -> ""
}
T is inferred as String (thanks of the bar != null case of the when expression) and the compiler does not complain about this.capitalize() invocation in the block passed to runCatching.
I hope this can help you, both as an approach than allows you to solve the problem and as explanation of the problem itself.
I was surprised today to learn that this take on apparently idiomatic code fails:
class QuickTest {
var nullableThing: Int? = 55
var nullThing: Int? = null
#Test
fun `test let behaviour`() {
nullableThing?.let {
print("Nullable thing was non-null")
nullThing?.apply { print("Never happens") }
} ?: run {
fail("This shouldn't have run")
}
}
}
It happens because, combined with implicit return, nullThing?.apply{...} passes null to the let, and therefore the elvis operator evaluates on null and runs the second block.
This is pretty horrible to detect. Do we have an appropriate alternative beyond conventional if/else without this pitfall?
You could use also instead of let. also will return nullableThing, whereas let will return whatever the lambda returns.
See this article: https://medium.com/#elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84 (point "3. Return this vs. other type").
Your case is a candidate for also thematic. Compare two block actions:
fun <T> T.also(block: (T) -> Unit): T
fun <T, R> T.let(block: (T) -> R): R
nullableThing?.also {
print("Nullable thing was non-null")
nullThing?.apply { println("Never happens") }
} ?: run {
fail("This shouldn't have run")
}
Another idiomatic way is to use when statement
when (nullableThing) {
null ->
print("Nullable thing was non-null")
nullThing?.apply { println("Never happens") }
else -> fail("This shouldn't have run")
}
I'd like to have an applyif to work like:
builder.applyif(<condition expression>) {
builder.set...
}
to be equal with:
builder.apply {
if (<condition expression>) {
builder.set...
}
}
Is that possible?
Yes, of course. You can nearly program anything, but don't reinvent the wheel. Look at the bottom of the answer to see a standard Kotlin approach without own extension function(s) which may already suffice your needs (not exactly applyIf though).
Now, however, lets see how an applyIf might be implemented:
inline fun <T> T.applyIf(predicate: T.() -> Boolean, block: T.() -> Unit): T = apply {
if (predicate(this))
block(this)
}
Don't forget the inline if you are implementing extension functions with lambdas.
Here is an example usage of the above.
// sample class
class ADemo {
fun isTrue() = true
}
// sample usage using method references
ADemo().applyIf(ADemo::isTrue, ::println)
// or if you prefer or require it, here without
ADemo().applyIf( { isTrue() } ) {
println(this)
}
If you just want to supply a boolean instead, you can use the following extension function:
inline fun <T> T.applyIf(condition : Boolean, block : T.() -> Unit) : T = apply {
if(condition) block(this)
}
and call it with:
val someCondition = true
ADemo().applyIf(someCondition) {
println(this)
}
And now a possible Kotlin standard way with which more people could be familiar:
ADemo().takeIf(ADemo::isTrue)
?.apply(::println)
// or
ADemo().takeIf { it.isTrue() }
?.apply { println(this) }
If they do remember (I actually didn't until I saw Marko Topolniks comment) they should immediately know what's going on.
However, if you require the given value (i.e. ADemo()) after calling takeIf this approach might not work for you as the following will set the variable to null then:
val x = ADemo().takeIf { false }
?.apply { println(this) /* never called */ }
// now x = null
whereas the following will rather set the variable to the ADemo-instance:
val x = ADemo().applyIf(false) { println(this) /* also not called */ }
// now x contains the ADemo()-instance
Chaining the builder calls might not be so nice then. Still you can also accomplish this via standard Kotlin functions by combining the takeIf with apply or also (or with, let, run, depending on whether you want to return something or not or you prefer working with it or this):
val x = builder.apply {
takeIf { false }
?.apply(::println) // not called
takeIf { true }
?.apply(::println) // called
}
// x contains the builder
But then again we are nearly there where you were already in your question. The same definitely looks better with applyIf-usage:
val x = builder.applyIf(false, ::println) // not called
.applyIf(true) {
println(this) // called
}
// x contains the builder
Sure you can, you just need an extension function so you can call it on the builder, and you need it to take a Boolean parameter and the lambda to execute.
If you look at the source of the apply function itself, it will help with most of the implementation:
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
Based on this, applyIf can be as simple as:
inline fun <T> T.applyIf(condition: Boolean, block: T.() -> Unit): T {
return if (condition) this.apply(block) else this
}
Usage looks like this:
builder.applyIf(x > 200) {
setSomething()
}
fun <T> T.applyIf(condition: Boolean, block: T.() -> T) = if (condition) block() else this
fun main() {
println("a".applyIf(true) { uppercase() }) // A
println("a".applyIf(false) { uppercase() }) // a
}
I am trying to perform a simple side-effect in Kotlin:
fun handle(request: Request) {
repository.findByUID(request.userId)?.let {
if (someCondition) return
service.run(...)
}
}
As you can see, the side-effect should be performed when the repository returns a non-null value and when someCondition is satisfied.
Is there any Kotlin-way of doing this rather than using if{}-return constructs?
In Java 8, it could be achieved by:
optional
.filter(...)
.ifPresent(...)
Update:
Kotlin 1.1 has a method called takeIf:
/**
* Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
*/
#kotlin.internal.InlineOnly
#SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
You can use it this way:
repository.findByUID(request.userId)?.takeIf { someCondition }?.let { service -> }
Kotlin doesn't contain such method in the stdlib.
However, You can define it:
inline fun <K : Any> K.ifPresent(condition: K.() -> Boolean): K? = if (condition()) this else null
Using this method your example can be rewritten as:
fun handle(request: Request) {
repository.findByUID(request.userId)?.ifPresent { someCondition }?.let {
service.run(...)
}
}
Another option may be to use the built in extensions for list (but there is an overhead of using lists):
listOf(repository.findByUID(userId)).filter { someCondition }.forEach { service.run(...) }
Kotlin's nullable types are very similar to Java's Optional (which is very similar to Guava's Optional).
In Kotlin 1.1 you can use takeIf which "is like filter for a single value" (takeIf() and also() - What's New in Kotlin 1.1 - Kotlin Programming Language):
repository.findByUID(request.userId).takeIf { !someCondition }?.let { service.run(...) }
Kotlin 1.0 does not define map, flatMap, filter/takeIf, etc. for nullable types but you can easily define your own function. e.g.:
inline fun <T> filter(value: T?, predicate: (T) -> Boolean): T? {
return if (value != null && predicate(value)) value else null
}
Example usage:
filter(repository.findByUID(request.userId)) { !someCondition }?.let { service.run(...) }
I would go without extra libs nor extension functions with this construct:
?.let { if (someCondition) null else it }
After aplying this construct on the code sample from the original question, it would look like:
fun handle(request: Request) {
repository.findByUID(request.userId)
?.let { if (someCondition) null else it }
?.let {
service.run {
/* ... */
}
}
}
Or at least it looks OK, compiles and have same types in my codebase after defining Request, repository, findByUid etc. :-)
This declaration works, but is not the most beautiful code. Is there a way to return functions less ugly? I tried (s: String) -> writer.println(s) but this didn't work.
val writeStuff: (PrintWriter) -> (String) -> Unit = {
val writer = it
val f: (String) -> Unit = {
writer.println(it)
}
f
}
PrintWriter("test").use { writeStuff(it)("TEST") }
EDIT: a bit more concrete example:
val writeStuff: (PrintWriter) -> (String) -> Unit = { writer ->
{ writer.println(it) }
}
val sendStuff: (Any) -> (String) -> Unit = { sender ->
{ sender.equals(it) }
}
#Test fun test1() {
val li = listOf("a", "b", "c")
val process: List<(String) -> Unit> =
listOf(writeStuff(PrintWriter("a")), sendStuff(Object()))
process.map { li.map(it) }
}
First, you can simplify your code using lambda syntax with explicit parameter and inlining val f:
val writeStuff: (PrintWriter) -> (String) -> Unit = { writer ->
{ writer.println(it) }
}
But since Kotlin supports local function declarations, you can even make writeStuff a local fun instead of a val.
This would lead to the following code:
fun writeStuff(writer: PrintWriter): (String) -> Unit {
return { writer.println(it) }
}
Or, using the single expression syntax,
fun writeStuff(writer: PrintWriter): (String) -> Unit = { writer.println(it) }
The usage, however, will be the same:
PrintWriter("...").use { writeStuff(it)("...") }
I stumbled across this question while trying to figure out how to return a Function (the java interface) in Kotlin. While this doesn't directly answer the question, hopefully it'll help someone else who has the same query:
override fun myFun(param1: Object): Function<in Object, out String?> {
if (!param1.meetsCriteria())
return Function { obj -> null }
return Function { obj ->
"success"
}
}
In this case, I was overriding a method in a java interface that required me to return a Function instance. (Note that since the param is not used in my particular implementation above, I could remove it and just have the return result. eg return Function { null })
Edit: After some research, it turns out Kotlin covers this subject with their discussion on "SAM (single abstract method) conversions" here and here, though it may not be the most intuitive thing to look up when figuring out how to return Functions.