How does the Kotlin compiler decide whether an expression enclosed in { } is a block or a lambda?
Consider this:
val a: Int
if (cond)
a = 1
else
a = 2
can be written more succinctly as:
val a =
if (cond)
1
else
2
Similarly, one might think that this:
val a: () -> Int
if (cond)
a = { 1 }
else
a = { 2 }
should be able to be written more succinctly like this:
val a =
if (cond)
{ 1 }
else
{ 2 }
But this is not the same: a is now an Int, and not of type () -> Int, because now the { 1 } is no longer a lambda. What are the rules that say whether something is a lambda or a block?
I didn't look into the Kotlin lexer, but I guess there are few possible places in the code where the compiler expects either a single expression or a block of code. That includes the code immediately following most of control flow statements like: if, else, while, when (one of its cases), etc. If you put { in one of these places, it will be interpreted as a start of the block of code related to this control flow statement and not as a lambda.
This is as simple as that. Note that even if you hint the compiler about the type, it still won't work:
// compile error
val a: () -> Int = if (cond)
{ 1 }
else
{ 2 }
It will be interpreted more like this:
// compile error
val a: () -> Int = if (cond) {
1
} else {
2
}
{ after if condition is always interpreted as a start of block of code. You need to put double {, } in cases like this:
// works fine
val a: () -> Int = if (cond) {
{ 1 }
} else {
{ 2 }
}
To put it very succinctly and easy to remember, the first opening brace after an if/when/else/for is always assumed to be the opening of a block. Use double braces if you want a lambda in there.
This is specified in the Kotlin Language Specification, section 1.2: Syntax Grammar:
The grammar defines a block as statements between { and }. In most cases (such as function bodies) the syntax is different between a block and a lambda. Where it is the same is in the usage of controlStructureBody - these are the places where the block has a value, or where you could put a non-block expression in its place. If you search the whole spec document for "controlStructureBody", you'll find it's used in the following places:
For statement
While statement
Do-while statement
If expression
When expression
When entry
In every other place where a value is required, a '{' signifies the start of a lambda.
Related
It would be a basic question, but I couldn't figure out a solution. I need to initialize a constant out of the right-side value of below either type.
val test: Either<String, Int> = 1.right()
I tried something like below but it shrinks the scope of the constant.
when(test) {
is Either.Right -> {val get:Int = test.b}
is Either.Left -> println(test.a)
}
I want that get to be scoped outside of when statement. Is there any way to do it or Arrow Either is not made for this purpose?
The important question is: what should happen if the Either is Left. In this example it is created close to where it's used, so it is obvious to you as a developer. But to the compiler what is inside the Either can be either an Int or a String.
You can extract the value using for example fold:
val x = test.fold({ 0 }, {it}) // provide 0 as default in case the Either was a `Left`
// x = 1
another option is getOrElse
val test = 1.right()
val x = test.getOrElse { 42 } // again, default in case it was a `Left`
// x = 42
You can also work with it without unwrapping it:
val test = 1.right()
val testPlus10 = test.map { it + 10 } // adds 10 to `test` if it is `Right`, does nothing otherwise
val x = testPlus10.getOrElse { 0 } // unwrap by providing a default value
// x = 11
For more example check the official docs.
Recommended reading: How do I get the value out of my Monad
What is it about Iterable.fold(...) in Kotlin that allows me to put the operation function after the closing parentheses?
val numbers = listOf(5, 2, 10, 4)
// operation function passed as the second param of fold
val sumDoubled1 = numbers.fold(0, { sum, n -> sum + n * 2 })
println(sumDoubled1)
// operation function after the closing paren of fold
val sumDoubled2 = numbers.fold(0) { sum, n -> sum + n * 2 }
println(sumDoubled2)
Further to Pavneet's answer, the rationale behind this that it allows you to write what look like language extensions. For example:
repeat (10) {
// Do something
}
That looks like a new type of loop structure; but it's really just a function called repeat() that takes two parameters; an integer, and a lambda.
Also, if the lambda is the only parameter, you can omit the parens entirely, e.g.:
repeatForever {
// Do something
}
(repeat() is in the standard library; repeatForever() is left as an exercise for the reader :-)
The ability to neaten some inline method calls, such as someValue.takeIf{ it > 0 } is just a nice side-effect of that.
It is called Passing trailing lambdas means, if a methods takes last parameter input as function(aka method literal) then it can be placed outside that method call though you can also place it inside the brackets as well. A simple example would be:
fun main() {
processInput("Lambda", { println(it) })
processInput("Passing trailing lambda") { println(it) }
processInput("Passing trailing lambda with named param") { input -> println(input) }
}
fun processInput(input:String, method:(str:String)->Unit){
method(input.toUpperCase()) // additional logic
}
Output:
LAMBDA
PASSING TRAILING LAMBDA
PASSING TRAILING LAMBDA WITH NAMED PARAM
1.Can I simplify the code in when statement to one or two lines.I am trying to replace the code in the when block but unable to do it.
// Loads all the settings changed in the theme
override fun onCreate() {
super.onCreate()
var sharedPreferences = getSharedPreferences(
CatalystConstants.keyThemeObject,
AppCompatActivity.MODE_PRIVATE
)
when (sharedPreferences.getInt(CatalystConstants.prefName, CatalystConstants.themeLight)) {
CatalystConstants.themeLight -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}
CatalystConstants.themeDark -> {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
}
}
}```
If there are only two possible values, you can do this, although in my opinion when would be much clearer to read because the comparison is so long.
appCompatDelegate.defaultNightMode = if(sharedPreferences.getInt(CatalystConstants.prefName, CatalystConstants.themeLight)
== CatalystConstants.themeLight)
AppCompatDelegate.MODE_NIGHT_NO
else // themeDark
AppCompatDelegate.MODE_NIGHT_YES
With when:
appCompatDelegate.defaultNightMode =
when (sharedPreferences.getInt(CatalystConstants.prefName, CatalystConstants.themeLight)) {
CatalystConstants.themeLight -> AppCompatDelegate.MODE_NIGHT_NO
CatalystConstants.themeDark -> AppCompatDelegate.MODE_NIGHT_YES
}
Could be more concise using run. It's not very clear, but that's because you aren't using all caps for your constant names:
CatalystConstants.run {
appCompatDelegate.defaultNightMode =
when (sharedPreferences.getInt(prefName, themeLight)) {
themeLight -> AppCompatDelegate.MODE_NIGHT_NO
themeDark -> AppCompatDelegate.MODE_NIGHT_YES
}
}
There is not if-else ternary expresion in Kotlin but you can write if-else statment in this way:
if (a > b) a else b
It's the most similar way to ternary if-else
This question already has answers here:
Swift 'if let' statement equivalent in Kotlin
(16 answers)
Closed 4 years ago.
let say I have this variable
val number : Int? = 12
I want to make control flow based on the variable, if the variable is null then do something, otherwise do something else
in Swift I can make something like this:
if let number = number {
print(number)
} else {
// do something else
}
I actually can do like this
if number != null {
print(number!!) // I want to avoid exclamation mark like this
} else {
// do something else
}
but I want to avoid exclamation mark, like in print(number!!)
I previously though that I can do something like this
number?.let {
print(it)
} else {
// else block is not available in let in Kotlin
}
so how to solve this ?
The ?. means it will be executed only if the left side is not null.
The ?: operator executes the right side only if the left side is not null.
You may have something like:
theExpression().toCompute().theNumber?.let { number ->
print("This is my number $number
} ?: run {
print("There is no number")
}
Here we use T.let extension function for the then clause and run{ } (extension) function for the else clause.
Warning: The semantics is that you expected to return non-null value from the let {...} closure to avoid the `run {..} closure from being executed. Thus the code like:
number?.let { null } ?: run { 42 } === 42 (and not null)
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()