What does the arrow ("->") operator do in Kotlin? - operators

Probably a little bit broad question, but the official documentation doesn't even mentioning the arrow operator (or language construct, I don't know which phrase is more accurate) as an independent entity.
The most obvious use is the when conditional statement, where it is used to assign an expression to a specific condition:
val greet = when(args[0]) {
"Appul" -> "howdy!"
"Orang" -> "wazzup?"
"Banan" -> "bonjur!"
else -> "hi!"
}
println(args[0] +" greets you: \""+ greet +"\"")
What are the other uses, and what are they do?
Is there a general meaning of the arrow operator in Kotlin?

The -> is part of Kotlin's syntax (similar to Java's lambda expressions syntax) and can be used in 3 contexts:
when expressions where it separates "matching/condition" part from "result/execution" block
val greet = when(args[0]) {
"Apple", "Orange" -> "fruit"
is Number -> "How many?"
else -> "hi!"
}
lambda expressions where it separates parameters from function body
val lambda = { a:String -> "hi!" }
items.filter { element -> element == "search" }
function types where it separates parameters types from result type e.g. comparator
fun <T> sort(comparator:(T,T) -> Int){
}
Details about Kotlin grammar are in the documentation in particular:
functionType
functionLiteral
whenEntry

The -> is a separator. It is special symbol used to separate code with different purposes. It can be used to:
Separate the parameters and body of a lambda expression
val sum = { x: Int, y: Int -> x + y }
Separate the parameters and return type declaration in a function type
(R, T) -> R
Separate the condition and body of a when expression branch
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
Here it is in the documentation.

From the Kotlin docs:
->
separates the parameters and body of a lambda expression
separates the parameters and return type declaration in a function
type
separates the condition and body of a when expression branch

Related

Is return inside function definition is also a expression in kotlin

fun trueOrFalse(exp: Boolean): String {
if (exp) return "It's true!"
return "It's false"
}
I was reading the "atomic kotlin" book where it says this function contains 2 expression so I just wanted to is function return also expression in kotlin ?
Unlike most programming languages, Kotlin treats return ... not as a statement but as an expression.
The type that the compiler infers for such expressions is Nothing, which means that the expression never evaluates to anything, and the control flow never continues normally after such an expression (similar to throw-expressions).
An example that demonstrates that it's an expression would be:
val x = when (coin) {
0 -> 123
1 -> (return 456) as Nothing
else -> error("unexpected coin value")
}
(runnable sample)
With the type of a return ... expression being Nothing, it doesn't make a lot of sense to use it as a part of other composite expressions. However, there are cases when it's convenient:
val x = if (foo) bar() else return baz()
val x = foo() ?: return bar()
Fun fact: expressions like return return return 5 are valid, although they trigger a compiler warning for unreachable code.

Understanding a lambda construct that contains dot followed by brackets

This is the function declaration for rememberCoilPainter:
#Composable
fun rememberCoilPainter(
request: Any?,
imageLoader: ImageLoader = CoilPainterDefaults.defaultImageLoader(),
shouldRefetchOnSizeChange: ShouldRefetchOnSizeChange = ShouldRefetchOnSizeChange { _, _ -> false },
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
fadeIn: Boolean = false,
fadeInDurationMs: Int = LoadPainterDefaults.FadeInTransitionDuration,
#DrawableRes previewPlaceholder: Int = 0,
): LoadPainter<Any> {
}
The line of code I am having difficulty understanding is:
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null
A dot appears after Builder followed by (size: IntSize)
This is the first time I've seen this construct in Kotlin and am not sure how to interpret it. This is a lambda. Normally the dot after an object refers to a sub component of a class or a package. But the ( ) after the dot isn't clear.
How do I implement the requestBuilder parameter?
This is a function with receiver type as described here: https://kotlinlang.org/docs/lambdas.html#function-types
Function types can optionally have an additional receiver type, which is specified before a dot in the notation: the type A.(B) -> C represents functions that can be called on a receiver object of A with a parameter of B and return a value of C. Function literals with receiver are often used along with these types.
It could be tricky to understand at first, but this is like you are providing a function/lambda that is a method of ImageRequest.Builder. Or in other words: your lambda receives one additional parameter of type ImageRequest.Builder and it is available in the lambda as this.
You can provide requestBuilder as any other lambda, but note that inside it you will have access to properties and methods of ImageRequest.Builder object that was provided to you.
What you are looking at is a "function literal with receiver". Speaking generically, a type A.(B) -> C represents a function that can be called on a receiver object of A with a parameter of B and return a value of C. Or in your example:
requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)?
We have a function requestBuilder which can be called on a ImageRequest.Builder with a parameter size: IntSize and returns another ImageRequest.Builder.
Calling this function is just like calling any other function with a lambda as a parameter. The difference: You have access to ImageRequest.Builder as this inside your lambda block.
Hope the following example helps understand lambdas with receiver type:
data class Person(val name: String)
fun getPrefixSafely(
prefixLength: Int,
person: Person?,
getPrefix: Person.(Int) -> String): String
{
if (person?.name?.length ?: 0 < prefixLength) return ""
return person?.getPrefix(prefixLength).orEmpty()
}
// Here is how getPrefixSafely can be called
getPrefixSafely(
prefixLength = 2,
person = Person("name"),
getPrefix = { x -> this.name.take(x) }
)
How do I implement the requestBuilder parameter?
Hope this part of the code snippet answers the above:
getPrefix = { x -> this.name.take(x) }
PS: These lambdas with receiver types are similar to extension functions IMO.

Distinguish functions with lambda argument by lambda's return type?

I have a function timeout(...) (extension function that returns this) which accepts an argument that is either String, Date or Long. What I am trying to do is to make it accept any lambda that also returns one of these three types.
Kotlin finds the below functions ambiguous and can't decide which one to call when I type, for example, timeout { "something" }.
#JvmName("timeoutString")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->String): CR = timeout(timeLambda())
#JvmName("timeoutLong")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->Long): CR = timeout(timeLambda())
#JvmName("timeoutDate")
fun <CR: CachableResponse> CR.timeout(timeLambda: CR.()->Date): CR = timeout(timeLambda())
The error I'm getting is Cannot choose among the following candidates without completing type inference.
Of course one way to work around this, is to have one function instead of three like this:
fun <CR: CachableResponse, Type> CR.timeout(timeLambda: CR.()->Type): CR =
timeLambda().let { when (it) {
is String -> timeout(it)
is Date -> timeout(it)
is Long -> timeout(it)
else -> this
} }
In this case, though, the developer won't have any clue what its lambda will have to return without reading the description or checking the source code.
Is there any more elegant solution?
Actually, you solution is rather elegant.
I would only suggest to inline CR generic parameter and capture when subject in a variable:
fun <Type> CachableResponse.timeout(timeLambda: CachableResponse.() -> Type) =
when (val it = timeLambda()) {
is String -> timeout(it)
is Date -> timeout(it)
is Long -> timeout(it)
else -> this
}
In this case, though, the developer won't have any clue what its lambda will have to return without reading the description or checking the source code.
IDE comes to the rescue:

kotlin object conversion in lambdas convert

I'm trying to have this compiling:
val criteriaList = aList.stream().map { dateRange -> {
Criteria.where("KEY").`is`(dateRange) } }.toList().toTypedArray()
Criteria().orOperator(*criteriaList)
But:
Criteria().orOperator(*criteriaList)
Currently does not compile:
Type mismatch.
Required:
Array<(out) Criteria!>!
Found:
Array<(() → Criteria!)!>
Why?
You are mapping your dateRange to a () -> Criteria.
You do not need to wrap what is following after -> with curly braces. Check also the Kotlin reference regarding Lambda expression syntax:
val sum = { x: Int, y: Int -> x + y }
A lambda expression is always surrounded by curly braces [...], the body goes after an -> sign. If the inferred return type of the lambda is not
Unit, the last (or possibly single) expression inside the lambda body is treated as the return value.
So you could just write the following instead:
.map { dateRange -> Criteria.where("KEY").`is`(dateRange) }
Note also that you do not really need to call stream(), but you can directly call map on it (except it wouldn't be a real List in the first place).
So your code could probably be simplified to something like:
val criteriaList = aList.map { dateRange -> Criteria.where("KEY").`is`(dateRange) }
.toTypedArray()
or
val criteriaList = aList.map { Criteria.where("KEY").`is`(it) }
.toTypedArray()

Convert from char to operator Kotlin

Is there any way to convert a char, lets say with a value of '+', into the operator +? Something like this:
println(1 charOperator 1);
output:
2
You can use something like this:
fun operatorFromChar(charOperator: Char):(Int, Int)->Int
{
return when(charOperator)
{
'+'->{a,b->a+b}
'-'->{a,b->a-b}
'/'->{a,b->a/b}
'*'->{a,b->a*b}
else -> throw Exception("That's not a supported operator")
}
}
and later call:
println(operatorFromChar('+').invoke(1,1))
Operators are, at the end of the way, functions. If you return a function with the operator's job, you can invoke it as it was the operator itself, but it will never be as "pretty" as calling the operator directly.
This isn't really possible. Maybe you should add your current solution and there's another way to help you out.
Here's a sneaky solution for calculating expressions with + and - only:
val exp = "10+44-12+3"
val result = exp.replace("-", "+-").split("+").sumBy { it.toInt() }
You can do something like
infix fun Int.`_`(that: Int) = this + that
where the backtick is unnecessary to this character but maybe necessary for other character. Then you can try:
println(2 _ 3) // output: 5
Update according to the comment:
I mean something like
val expr = input.split(' ')
when (expr[1])
{
'+' -> return expr[0].toInt() + expr[2].toInt()
'-' -> return expr[0].toInt() - expr[2].toInt()
'*' -> return expr[0].toInt() * expr[2].toInt()
'/' -> return expr[0].toInt() / expr[2].toInt()
// add more branches
}
However, I was wondering whether there is a better and tricky solution from the grammar of Kotlin.
What you basically want is an Char to result of an operation mapping. So, I decided to return the result right away and not a lambda.
fun Int.doOperation(charOperator: Char, x: Int) = when(charOperator) {
'+' -> this + x
'-' -> this - x
'/' -> this / x
'*' -> this * x
else -> throw IllegalArgumentException("Not supported")
}
Using an extension function maybe (?) makes the syntax a little nicer. You decide.
Call site:
println(5.doOperation('+', 6))
You can use the interpreter class in beanshell library
to convert string text automatically to result
for example
interpreter.eval("equal=2*3")
println(interpreter.get("equal").toString().toDouble().toString())
or can use expression class that does the same thing
fun String.stringToConditionalOperators(): (Boolean, Boolean) -> Boolean {
return when (this.lowercase(Locale.getDefault())) {
"and" -> {
{ a: Boolean, b: Boolean ->
a && b
}
}
"or" -> {
{ a: Boolean, b: Boolean ->
a || b
}
}
// You can add more operator 🤩
else -> {
return { a: Boolean, b: Boolean ->
a || b
}
}
}
}
Usage..
val operator = "AND"
operator.stringToConditionalOperators().invoke(one, two)