Sunday, July 31, 2022

Learn the same old thing again - Kotlin

 As you might have guessed, I got very busy between March 2021 & March 2022. In fact I have been very busy at work & play between March 2020 & January 2022. Remote work has its own challenges. But work from office is even more hassle.

The last time I learned Kotlin was in July 2019. 3 years later, I am ready to learn it again with all the new features but for backend software development this time. For me, Kotlin is a combination of 4 languages now - Java, Python, Golang & Swift.

Kotlin is so much fun, because we get to write so many "fun"s & the capability to write extensions on top of them.


One feature I stumbled across and got surprised at is the ability to use data types as variable names as you can see in the example above.

Code:
fun main() {
    var MSG = "Hello"
    val Int = 5
    val String = "World"
    val Boolean = false
    println(Int)
    println(String)
    println(!Boolean)
    MSG+=','
    var greeting = "$MSG world!"
    println(greeting)
}

Output:
5
World
true
Hello, world!

Functions are fun

A function with two Int parameters and Int return type.

A function body can be an expression. Its return type is inferred.

A function that returns no meaningful value.

Unit return type can be omitted.

In Kotlin, if can also be used as an expression.

There is a new thing worth learning now. It is called the "when" expression. Seems pretty similar to "switch" case.
fun sum(a: Int, b: Int): Int = a + b
fun sayHello(): Unit = println("Hello")
fun isPositive(number: Int): Boolean = number > 0

Return type can be inferred and therefore omitted when feasible

fun sum(a: Int, b: Int) = a + b // Int
fun sayHello() = println("Hello"// Unit
fun isPositive(number: Int) = number > 0 // Boolean

If you don't like for, while & do-while, or find it all too confusing, here is a new way to use loops - repeat

fun main() {
    repeat(3) {
        println("Hello")
    }
}
fun main() {
repeat(5) {
println("Kotlin")
}
}

If you don't like writing too much to make code modular, put functions aside, make way for lambda expressions.

fun(a: Int, b: Int): Int {
    return a * b
}

{ a: Int, b: Int -> a * b }
To invoke it inline,
println({ a: Int, b: Int -> a * b } (5,3))
We can assign the function to a variable and then invoke it by invoking the variable.

val mul1 = fun(a: Int, b: Int): Int {
    return a * b
}

val mul2 = { a: Int, b: Int -> a * b }

println(mul1(23))  // prints "6"
println(mul2(23))  // prints "6" too

Lambda can help save lines of code as well as impact code readability.

fun isNotDot(c: Char): Boolean = c != '.'
val originalText = "I don't know... what to say..."
val textWithoutDots = originalText.filter(::isNotDot)


val originalText = "I don't know... what to say..."val textWithoutDots = originalText.filter({ c: Char -> c != '.' })
val textWithoutDots = originalText.filter(){ c -> c != '.' }val textWithoutDots = originalText.filter { c -> c != '.' }val textWithoutDots = originalText.filter { it != '.' }

fun sum(a: Int, b: Int): Int = a + b
val mul2 = { a: Int, b: Int -> a * b }

Functions can be returned as objects

fun identity(x: Int) = x
fun half(x:Int) = x/2
fun zero(x:Int) = 0

fun generate(functionName: String): (Int) -> Int {
if (functionName == "identity") return ::identity
else if (functionName == "half") return ::half
else return ::zero//if (functionName == "zero")
}

fun composition(value: Int, y: (Int) -> Int, g: (Int) -> Int): Int {
return y(g(value))
}

Double Dots & IN for Ranges
Kotlin allows generating ranges and checking inclusion in ranges using .. & in.

If we need to check that a value is not within a range, we can just add ! (not) before in.

val within = 10 !in 5..50 // true
val notWithin = 100 !in 10..99 // true

We may combine ranges using standard logical operators.

val within = c in 5..10 || c in 20..30 || c in 40..50

You can assign a range to a variable and use it later.

val range = 100..200
println(300 in range) // false

In addition to integer ranges, we can also use ranges of characters and even strings (according to dictionary order).

println('b' in 'a'..'c'// true
println('k' in 'a'..'e'// false

println("hello" in "he".."hi"// true
println("abc" in "aab".."aac"// false

That's gonna be some fun for permutations & combinations.

When in dots, have fun!

val result = when (op) {
    "+" -> a + b
    "-" -> a - b
    "*" -> a * b
    else -> "Unknown operator"
}
println(result)
when (op) {
    "+""plus" -> println(a + b)
    "-""minus", -> println(a - b) // trailing comma
    "*""times" -> println(a * b)
    else -> println("Unknown operator")
}
when (op) {
    "+""plus" -> {
        val sum = a + b
        println(sum)
    }
    "-""minus" -> {
        val diff = a - b
        println(diff)
    }
    "*""times" -> {
        val product = a * b
        println(product)
    }
    else -> println("Unknown operator")
}
println(when(op) {
    "+" -> a + b
    "-" -> a - b "*" -> a * b
    else -> "Unknown operator"
})
when (n) {
    0 -> println("n is zero")
    in 1..10 -> println("n is between 1 and 10 (inclusive)")
    in 25..30 -> println("n is between 25 and 30 (inclusive)")
    else -> println("n is outside a range")
}
when {
        n == 0 -> println("n is zero")
        n in 100..200 -> println("n is between 100 and 200")
        n > 300 -> println("n is greater than 300")
        n < 0 -> println("n is negative")
}
    when(readln().toInt()) {
in 0..9 -> println(1)
in 10..99 -> println(2)
in 100..999 -> println(3)
in 1000..9999 -> println(4)
}

fun Request.getBody() = when (val response = executeRequest()) { is Success -> response.body is HttpError -> throw HttpException(response.status) }

Functions can be passed as parameters to other functions
    fun odd(x: Int): Boolean = x % 2 == 1
    println(odd(6)) // => false
    println(odd(7)) // => true

// If the return type can be inferred then we don't need to specify it.
    fun even(x: Int) = x % 2 == 0
    println(even(6)) // => true
    println(even(7)) // => false

// Functions can take functions as arguments and return functions.
    fun not(f: (Int) -> Boolean): (Int) -> Boolean {
        return {n -> !f.invoke(n)}
    }
// Named functions can be specified as arguments using the :: operator.
    val notOdd = not(::odd)
    val notEven = not(::even)
    // Lambda expressions can be specified as arguments.
    val notZero = not {n -> n == 0}
Extension functions
fun String.remove(c: Char): String {
        return this.filter {it != c}
    }
println("Hello, world!".remove('l')) // => Heo, word!

No comments:

Post a Comment

Going one step further with Kotlin & gRPC

Recently, I tried using Quarkus with Kotlin for grpc. I have worked with grpc for communication between microservices in Java & Golang. ...