## Chapter 7: Make it cheap
### Effective Kotlin
---
## Who am I?
- [Brandy :arrow_right: Github](https://github.com/hmchangm/getting-start-QK)
- [2022 鐵人賽 Quarkus, Kotlin 雲原生服務開發](https://ithelp.ithome.com.tw/users/20135701/ironman/5479)
---
### Type Safty :rocket:
### Make it Cheap :moneybag:
---
## Item 49: Inline Value Classes
----
### Domain Model with Primitive Type?

----
## Some Cases
```kotlin=
fun auth(userName: String, password: String) {
println("authenticating $userName.")
}
fun main() {
auth("user1", "12345")
//Compilable, thay are all String
auth("12345", "user1")
}
```
----
## Inline Value Classes
```kotlin=
@JvmInline
value class Password(val value: String)
@JvmInline
value class UserName(val value: String)
```
----
```kotlin=
fun auth(userName: UserName, password: Password) {
println("authenticating $userName.")
}
fun main() {
auth(UserName("user1"), Password("12345"))
//does not compile due to type mismatch
auth(Password("12345"), UserName("user1"))
}
```
##### [Reference Link](https://www.droidcon.com/2022/09/25/my-top-4-use-cases-for-kotlin-inline-classes/)
----
### 火星氣候探測者號
#### [1 牛頓力 != 1 磅](https://kt.academy/article/ek-value-classes)

> 327.6 million USD
----
### 編譯期優化
還原型別

----
#### 型別安全 = Cost Saving

---
### Item 39 : Use sealed classes and interfaces to express restricted hierarchies
----
```kotlin=
sealed interface BinaryTree
class Leaf(val value: Any?) : BinaryTree
class Node(val left: Tree, val right: Tree) : BinaryTree
sealed interface Either<out L, out R>
class Left<L>(val value: L) : Either<L, Nothing>
class Right<R>(val value: R) : Either<Nothing, R>
```
----
### Sealed Classes
Sum Type :arrow_right: 可以被窮舉

----

----
### 優雅的 if else
```kotlin=
val x : Either<Exception, String> = magic("2")
val value = when(x) {
is Either.Left -> when (x.value) {
is NumberFormatException -> "Not a number!"
is IllegalArgumentException -> "Can't take 0!"
}
is Either.Right -> "Got reciprocal: ${x.value}"
}
```
---
[2020 鐵人賽 Algebraic Data Type (ADT) by Yanbin](https://ithelp.ithome.com.tw/articles/10240987)
---
### Make it cheap
* High performance
* Memory comsumption
* Cost Saving
---
#### Item 47: Avoid unnecessary object creation
##### Memory Cost :moneybag:
* Object Self
* Object header : 16 bytes
* Object reference : 4 bytes
---
```kotlin=
class A
private val a = A()
// Benchmark result: 2.698 ns/op
fun accessA(blackhole: Blackhole) {
blackhole.consume(a)
}
// Benchmark result: 3.814 ns/op
fun createA(blackhole: Blackhole) {
blackhole.consume(A())
}
```
----
```kotlin=
// Benchmark result: 3828.540 ns/op
fun createListAccessA(blackhole: Blackhole) {
blackhole.consume(List(1000) { a })
}
// Benchmark result: 5322.857 ns/op
fun createListCreateA(blackhole: Blackhole) {
blackhole.consume(List(1000) { A() })
}
```
---
### Tips 1 : Singlton - Object Reuse
----
```kotlin=
sealed class LinkedList<T>
class Node<T>(val head: T,val tail: LinkedList<T>)
: LinkedList<T>()
class Empty<T> : LinkedList<T>()
// Usage
val list: LinkedList<Int> =
Node(1, Node(2, Node(3, Empty())))
val list2: LinkedList<String> =
Node("A", Node("B", Empty()))
```
----
```kotlin=
sealed class LinkedList<out T>
class Node<out T>( val head: T,
val tail: LinkedList<T>
) : LinkedList<T>()
object Empty : LinkedList<Nothing>()
val list: LinkedList<Int> =
Node(1, Node(2, Node(3, Empty)))
val list2: LinkedList<String> =
Node("A", Node("B", Empty))
```
----
```kotlin=
sealed interface AdView
object FacebookAd : AdView
object GoogleAd : AdView
class OwnAd(val text: String,val imgUrl: String):AdView
```
---
#### Tips#2 - Cache
```kotlin=
private val FIB_CACHE: MutableMap<Int, BigInteger> =
mutableMapOf<Int, BigInteger>()
fun fib(n: Int): BigInteger = FIB_CACHE.getOrPut(n) {
if (n <= 1) BigInteger.ONE
else fib(n - 1) + fib(n - 2)
}
```
----
* Designing a cache well is not easy
* **Caffeine** and **Ehcache**,
* waiting for **support for suspending functions**
----
#### [Aedile](https://github.com/sksamuel/aedile) support suspending function
##### Aedile is a simple Kotlin wrapper for Caffeine which prefers coroutines
----
#### Cache RESTful Result
```kotlin=
val cache = caffeineBuilder<String, FruityVice> {
expireAfterWrite = 5.toDuration(DurationUnit.MINUTES)
}.build { findByName(it) }
// expensive call
private suspend fun findByName(name: String) =
suspend fun findByNameWithCache(name: String) =
cache.get(name)
```
---
#### Tips#3 - Heavy object lifting
```kotlin=
fun <T : Comparable<T>> Iterable<T>.countMax(): Int =
count { it == this.maxOrNull() }
fun <T : Comparable<T>> Iterable<T>.countMax(): Int {
val max = this.maxOrNull()
return count { it == max }
}
```
----
```kotlin=
fun String.isValidIpAddress(): Boolean {
return this.matches(
("""\A(?:(?:25[0-5]|2[0-4][0-9]|
[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|
[01]?[0-9][0-9]?)\z""").toRegex()
)
}
// Usage
print("5.173.80.254".isValidIpAddress()) // true
```
----
##### Lift out
```kotlin=
private val IS_VALID_IP_REGEX =
"""\A(?:(?:25[0-5]|2[0-4][0-9]|
[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|
[01]?[0-9][0-9]?)\z""".toRegex()
fun String.isValidIpAddress(): Boolean =
matches(IS_VALID_IP_REGEX)
```
---
#### Tips4 Lazy
`lazy()` is a function that takes a lambda and returns an instance of `Lazy<T>` The **first** call to get() executes the lambda passed to lazy() and remembers the result.
----
```kotlin=
object {
val objectMapper: ObjectMapper by lazy
{ Arc.container()
.instance(ObjectMapper::class.java).get() }
fun Any.toJson() : String =
objectMapper.writeValueAsString(this)
}
```
[Quarkus Sample](https://github.com/hmchangm/getting-start-QK/blob/master/src/main/kotlin/tw/brandy/ironman/resource/ResponseHandler.kt)
---
### Item 48 : Use inline modifier for functions
---
##### Inline function
nearly all Kotlin stdlib **higher-order functions** have an inline modifier
```kotlin=
inline fun repeat(times: Int,action: (Int) -> Unit) {
for (index in 0 until times) {
action(index)
}
}
```
----
```kotlin=
repeat(10) {
print(it)
}
//after compilation
for (index in 0 until 10) {
print(index)
}
```
---
#### type argument can be reified
```kotlin=
fun <T> printTypeName() {
print(T::class.simpleName) // ERROR
}
```
##### JVM erased generic types during compilation

----
#### inline + reified
```kotlin=
inline fun <reified T> printTypeName() {
print(T::class.simpleName)
}
// Usage
printTypeName<Int>() // Int
printTypeName<Char>() // Char
printTypeName<String>() // String
```
---
### reified with JsonDecode
```kotlin
inline fun <reified T> String.toType():Either<AppError, T>=
Either.catch {
objectMapper.readValue(this, T::class.java)
}.mapLeft { JsonDecodeFail(it) }
val user: Either<AppError,User> = userText.toType()
```
---
##### High order functions faster when inlined
違背了哪一條?
```kotlin=
val lambda: () -> Unit = {
// code
}
```
```java=
// compiled to JVM equivalent of
Function0<Unit> lambda = new Function0<Unit>() {
public Unit invoke() {
// code
}
};
```
---
### crossinline 與 noinline
* noinline - the λ argument should not be inlined
* crossinline - the λ argument should be inlined but non-local return is not allowed
----
```kotlin=
inline fun requestNewToken(
hasToken: Boolean, crossinline onRefresh: () -> Unit,
noinline onGenerate: () -> Unit
) {
if (hasToken) {
httpCall("get-token", onGenerate)
} else {
httpCall("refresh-token") {
onRefresh()
onGenerate()
}
}
}
```
----

----

----
#### noinline, crossinline → IntelliJ 會告訴你
----
#### 沒有 function type 的參數的 fun 不需要 inline

---
#### inline fun 時機
* 很常很常用的 function
* reified type function
* High ordered functoin
---
{%youtube RM7aulixXC8 %}
---
#### Item 50: Eliminate obsolete object references
```kotlin=
fun pop(): Any? {
if (size == 0)
throw EmptyStackException()
val elem = elements[--size]
elements[size] = null ← 避免孤兒
return elem
}
```
---

---

---
### Take Away
* Type Safe Domain Model
* Data Class
* Sealed Class (Interface)
* Inline Value Class
----
* Make it cheap
* 空間 - Avoid necessary object creation
* 時間 - Cache
* GC friedly
---
### 參考連結
[Kotlin Inline Value Class 與 Jackson Json Serialization](https://ithelp.ithome.com.tw/articles/10309687)
https://www.youtube.com/watch?v=0JRPA0tt9og
https://www.youtube.com/watch?v=SIr7mcnVy98
{"metaMigratedAt":"2023-06-17T14:05:59.255Z","metaMigratedFrom":"YAML","title":"Chapter 7 - Make it cheap","breaks":true,"description":"View the slide with \"Slide Mode\".","contributors":"[{\"id\":\"28aef4d3-277e-46c5-887c-233edfe0da9f\",\"add\":15117,\"del\":5834}]"}