Kotlin: this expression in nested unnamed functions - kotlin

The Kotlin documentation describes how to access this expressions in nested classes.
Is it possible to access this expressions in nested unnamed functions? e.g:
str = "abcde"
str.run {
this.substring(0..2).run {
println("${this} is a substring of ${???}")
}

Just for the sake of answering the question as asked, for situations where this can improve readability, you can use a label:
str.run outer# { // Define a label
this.substring(0..2).run {
println("${this} is a substring of ${this#outer}") // Use the label
}
}
Oftentimes, you'll have implicit labels to use. For example, if the outer call is changed to use a different function name from the inner call, you can access it:
with (str) {
this.substring(0..2).run {
println("${this} is a substring of ${this#with}") // Use the implicit label
}
}

Related

how to apply two types to orElse() in Optional

I want to use orElse() function in Optional, but the first half "engineRepository.findById(engineId)" return Model, and second half return list of Model, how can I make the result return List?
I tried to add listof() to the first half, but I can't use orElse() anymore, how to do that?
var ModelList: List<Model> = try {
EngineRepository.findById(engineId).orElse(EngineRepository.findAllByEnvironment(environment)))
}
#Repository
interface EngineRepository : MongoRepository<Model, String> {
fun findAllByEnvironment(environment: String):List<Model>
}
You should indeed use listOf, but instead of passing the entire expression of EngineRepository.findById(engineId) to it, you should apply it to the Model wrapped inside the Optional returned by findById. To do that, use map:
EngineRepository.findById(engineId)
.map(::listOf)
.orElse(EngineRepository.findAllByEnvironment(environment))
Note that this will execute findAllByEnvironment whether or not findById returned an Optional.empty(). If you don't want that, you should use orElseGet:
.orElseGet { EngineRepository.findAllByEnvironment(environment) }

Can I convert the one line of also syntax into two lines with Kotlin?

The Code A is from https://github.com/mycwcgr/camera/blob/master/CameraXBasic/app/src/main/java/com/android/example/cameraxbasic/fragments/CameraFragment.kt
It's a little difficult to understand the also syntax for me, so I convert the Code A to the Code B.
I think that the Code B is the same as the Code A, right?
Code A
private fun bindCameraUseCases() {
val metrics = DisplayMetrics().also { viewFinder.display.getRealMetrics(it) }
...
}
Code B
private fun bindCameraUseCases() {
val metrics = DisplayMetrics()
viewFinder.display.getRealMetrics(metrics)
}
Yes, it is. What the also { } extension function does is perform actions defined in its block with the caller object as a parameter and return the caller.
val list = mutableListOf<Int>().also {
// the newly created empty `MutableList` is a parameter in this lambda
// and can be referred using the `it` identifier
it.add(1)
}
// is equivalent to
val list = mutableListOf<Int>()
list.add(1)
In this case, yes: your Code A and Code B do the same thing.  Within the lambda, it refers to the object that also was called on (the newly-created DisplayMetrics instance); and that's also what's returned.
And to answer your implied question: yes, in this case using also probably doesn't have much benefit!
It's more useful in the middle of a complex expression or return value, e.g.:
private fun getMetrics()
= DisplayMetrics().also{ println("Created metrics: $it") }
instead of:
private fun getMetrics(): DisplayMetrics {
val metrics = DisplayMetrics()
println("Created metrics: $metrics")
return metrics
}
Here it avoids an explicit local value, references to it, and an explicit return; once you're used to the idiom, it's simpler to read as well — especially when it's used for something like logging that's not part of the main program logic.
Kotlin's scoping functions (also, apply, let, run, with) can be a big confusing, but this page explains them fairly well.

How to refer to an outer function from a lambda?

The question is in the comment. I want to refer to the outer function append, and not the one that's defined in the StringBuilder, how do I do this?
fun append(c: Char) {
println("TEST")
}
fun sbTest() = with(StringBuilder()) {
for(c in 'A'..'Z') {
append(c) // how do I refer to the append function declared above?
}
toString()
}
I know I can introduce a function reference variable, like this:
val f = ::append
and call f instead of append, but is there another way?
The problem is that anything called within with shadows the outer functions, because this is introduced. The same problem appears if you have a class and a top-level function with a function with the same signature.
The obvious option would just be re-naming it. Also, the function you have there isn't really descriptive compared to what it actually does. But if you for some reason can't rename, there are still other options.
Top-level methods can be referenced by package in Kotlin, for an instance like com.some.package.method. It can also be imported as such, which is the most common way to do it. There are very few methods that are called as com.some.package.method().
Kotlin, like Python, allows as in imports. Which means, you can do this:
package com.example;
// This is the important line; it declares the import with a custom name.
import com.example.append as appendChar; // Just an example name; this could be anything ***except append***. If it's append, it defeats the purpose
fun append(c: Char) {
println("TEST")
}
fun sbTest() = with(StringBuilder()) {
for(c in 'A'..'Z') {
appendChar(c)
}
toString()
}
Alternatively, as I mentioned, you can add the package:
for(c in 'A'..'Z') {
com.example.append(c)
}
val f = ::append is of course an option too, either way, it is still easier to rename the function than create imports with as or constants, assuming you have access to the function (and that it doesn't belong to a dependency).
If your file is outside a package, which I do not recommend you do, you can just declare the import as:
import append as appendChar
You could also use an extension function instead of with(), such as .let{...}.
This will send StringBuilder as an argument to the extension function as it (You can rename it to whatever you want btw):
fun sbTest() = StringBuilder().let{ it ->
for(c in 'A'..'Z') {
// using string builder
it.append(c)
// using your function
append(c)
}
it.toString()
}
The .let{...} function returns your last statement, aka the String from toString(), so your original function would still return it properly. Other functions can return this instead, such as .also{...}
I tend to use extension functions rather than with() as they're more flexible.
See this post to master extension functions: https://medium.com/#elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84
EDIT: Got also{} and let{} mixed up. I switched them

Can you concatenate statements at run-time in Kotlin?

I am trying to interface with TeamCity using Kotlin-DSL
In this section of the TC Kotlin guide there is a rather odd looking part where it seems like it causes statements to become concatenated on the fly.
It first defines these:
val linux = Requirements() {
contains("os.name", "linux")
}
val oracle = Requirements() {
equals("db.name", "oracle")
}
val java6 = Requirements() {
contains("env.JAVA_HOME", "1.6")
}
Then does this with those definitions:
buildType {
...
requirements(linux + oracle + java6)
...
}
I know that the above section of code is equivalent to
buildType {
...
requirements {
contains("os.name", "linux")
equals("db.name", "oracle")
contains("env.JAVA_HOME", "1.6")
}
...
}
So I suppose what my question boils down to is what is the return type of the 'Requirements' function that can just be concatenated together? My guess is it is some sort of statement/ function wrapper and Kotlin lets you concatenate these as you go, and the function signature looks like this:
fun Requirements(init: (a: String, b: String) -> UnknownTypeA) : UnknownTypeB
EDIT:
For anyone who is confused when reading this in the future, the calls to Requirements are actually an object initialisation via the Requirements constructor. I do inevitably feel embarrassed for not picking up on this (The casing of the name should have been hint enough!) but I'm making this edit to make it clear to people that it is not a function. Thank you to Hotkey for pointing that out.
First, note that Requirements accepts a function into its constructor. Without knowing what is the type of that function, let's assume it's Context.() -> Unit (a function with receiver of Context, accepting no arguments and returning Unit).
Now, we can naturally overload the plus operator for the Requirements type, so that it returns another Requirements instance that has a function that applies both functions from the operands.
You could do that in your own code in the following way:
class Requirements(val check: Context.() -> Unit)
operator fun Requirements.plus(other: Requirements) =
Requirements { check(); other.check() }

returning the last expression in a block

I am learning the new and very beautiful language Kotlin and everything seems to be very logical and consistent. I only found one thing which seems like an arbitrary exception to the rule than a solid rule. But maybe I lack enough understanding some deeper reasons behind the rule.
I know that in if-else and when statements there are blocks of code, then the last expression is returned. In the next example 1 or 2 are returned depending on the condition - in our case it returns 1.
val x = if (1 < 2) {println("something"); 1} else {println("something else"); 2}
On the other hand this does not hold for any code block. The next line assigns y not to 1 but to the whole block of code as a lambda.
val y = {println("something"); 1}
Similarly in function body, the last expression is not returned. This does not even compile.
fun z() : Int {
println("something")
1
}
So what exactly is the rule? Is it really so arbitrary like: if in if-else or when statement which is used as expression there is a block of code, then the last expression in the block is returned. Otherwise the last expression is not returned to outer scope. Or am I missing something?
you misunderstand the curly brackets {}, when around with all flow-control statement it is just a block, for example:
if (condition) { //block here
}
WHEN the {} is declared separately it is a lambda expression, for example:
val lambda: () -> Int = { 1 }; // lambda
WHEN you want to return a lambda in if-else expression, you must double the curly brackets {} or using parentheses () to distinguish between the block and the lambda expression or make the {} as lambda explicitly, for example:
val lambda1: () -> Int = if (condition) { { 1 } } else { { 2 } };
val lambda2: () -> Int = if (condition) ({ 1 }) else ({ 2 });
val lambda3: () -> Int = if (condition) { -> 1 } else { -> 2 };
If a function does not return any useful value, its return type is Unit. Unit is a type with only one value - Unit. This value does not have to be returned explicitly.
On the other hand, a common function must have a explicit return statement if its return type if not a Unit:
fun z(): Int { return 1; }
Another case is a function return Nothing, the return statement don't allowed at all, because you can't create a Nothing instance, for example:
fun nothing(): Nothing {
return ?;// a compile error raising
}
WHEN a function has only one expression then you can using single-expression function instead, for example:
fun z() = 1;
There is a difference between a lambda block and just a 'normal' block, in your case the "y" is just a lambda that needs to be executed to get the returned value:
val block: () -> Int = { 5 }
val five: Int = { 5 }()
val anotherFive = block()
So if you want a block that acts as a lambda, you can create a lambda and execute it right away with "()". This way, your "z" function would compile like so:
fun z() : Int = {
println("something")
1
}()
(Which, of course, does not make much sense and is not very efficient)
You are absolutely right, V.K. The choice is completely arbitrary. Well, probably not completely arbitrary but related to parsing complexities arising from curly braces being chosen to denote both blocks and lambdas.
Actually, besides the case you make for block expressions, there also is the situation of (simple) blocks: just group of statements enclosed in braces. For example, Java supports (simple) blocks and you can write:
String toBeComputed ; {
// Relatively long sequence of operations here
toBeComputed= resultingValue ;
}
I use this idiom quite frequently because:
It's not always convenient to extract the code into a function.
Like a function, it delimits the code and documents its nature without introducing additional names or comments.
Sometimes it's preferable to initialize two or more variables in tandem and introducing a result-value class with this specific purpose feels like an overkill.
Also like a function it doesn't pollute the outer namespace with temporary variables only used internally.
Anecdotally, as Java does not support block expressions I would write the example as
int y ; {
println("something");
y= 1 ;
}
Other languages (ALGOL and Scala come to my mind) do support block expressions (and in the case of Scala also lambdas, but with a different syntax). In these languages what you proposed (in Scala syntax)
val y= { println("something"); 1 }
is completely valid and does not require () to force a lambda to evaluate (because there is no lambda!).
In conclusion, Kotlin designers did choose to make the language less consistent than ALGOL and Scala in terms of blocks and block expression, probably in favor of convenience.
I hope my long response shows that what you expected from Kotlin was not illogical, just not the case because of some language design choice. The principle of less surprise did not work this time.