Upgrade to Pro — share decks privately, control downloads, hide ads and more …

6. The Power of Inline [kotlin-workshop]

6. The Power of Inline [kotlin-workshop]

Part of https://github.com/jetBrains/kotlin-workshop.

Covers:
- Useful one-line library functions
- inline
- Reified Generics
- Collections vs Sequences

Svetlana Isakova

November 01, 2017
Tweet

More Decks by Svetlana Isakova

Other Decks in Programming

Transcript

  1. The power of inline

    View full-size slide

  2. Inlining
    Useful library functions
    Collections vs Sequences
    Agenda

    View full-size slide

  3. Under the Hood
    No performance overhead
    Lambdas can be inlined

    View full-size slide

  4. How inlining works?
    Why inline?..

    View full-size slide

  5. Useful library
    functions

    View full-size slide

  6. Useful library functions
    run, let, takeIf, takeUnless, repeat

    View full-size slide

  7. run function
    val foo = run {
    println("Calculating foo...")
    "foo"
    }
    runs the block of code (lambda)
    and returns the last expression
    as the result

    View full-size slide

  8. let function
    fun getEmail(): String?
    val email = getEmail()
    if (email != null) sendEmailTo(email)
    email?.let { e -> sendEmailTo(e) }
    getEmail()?.let { sendEmailTo(it) }
    allows to check the argument for being non-null,
    not only the receiver

    View full-size slide

  9. takeIf function
    val number = 42
    number.takeIf { it > 10 } // 42
    val other = 2
    other.takeIf { n -> n > 10 } // null
    returns the receiver object if it satisfies the given predicate,
    otherwise returns null

    View full-size slide

  10. takeUnless function
    val number = 42
    number.takeUnless { it > 10 } // null
    val other = 2
    other.takeUnless { it > 10 } // 2
    returns the receiver object if it does not satisfy the given
    predicate, otherwise returns null

    View full-size slide

  11. repeat function
    repeat(10) {
    println("Welcome!")
    }
    repeats an action for a given number of times

    View full-size slide

  12. They all are declared as
    inline functions
    inline fun run(block: () -> R): R = block()
    inline fun T.let(block: (T) -> R): R = block(this)
    inline fun T.takeIf(predicate: (T) -> Boolean): T? =
    if (predicate(this)) this else null
    inline fun T.takeUnless(predicate: (T) -> Boolean): T? =
    if (!predicate(this)) this else null
    inline fun repeat(times: Int, action: (Int) -> Unit)

    View full-size slide

  13. inline function
    compiler substitutes a body of
    the function instead of calling it

    View full-size slide

  14. inline fun T.takeUnless(predicate: (T) -> Boolean): T? =
    if (!predicate(this)) this else null
    fun foo(number: Int) {
    val result = number.takeUnless { it > 10 }
    ...
    }
    Generated code (in the bytecode):
    fun foo(number: Int) {
    val result = if (!(number > 10)) number else null
    ...
    }
    inlined code
    of lambda body

    View full-size slide

  15. inline fun synchronized(lock: Lock, action: () -> T): T {
    lock.lock()
    try {
    return action()
    } finally {
    lock.unlock()
    }
    }
    fun foo(lock: Lock) {
    synchronized(lock) {
    println("Action")
    }
    }
    fun foo(lock: Lock) {
    lock.lock()
    try {
    println("Action")
    } finally {
    lock.unlock()
    }
    }
    Generated code (in the bytecode):
    inlined code
    of lambda body

    View full-size slide

  16. No performance overhead
    when you use
    run, let, takeIf, takeUnless, repeat
    No anonymous class for lambda

    View full-size slide

  17. Reified generics

    View full-size slide

  18. Reified generics
    startActivity("ANSWER" to 42)
    val anys: List = listOf(1, "one", 2.0)
    val strings: List =
    anys.filterIsInstance() ["one"]

    View full-size slide

  19. Inlining lambda body
    val anys: List = listOf(1, "one", 2.0)
    val strings: List =
    anys.filterIsInstance()
    inline fun List<*>.filterIsInstance(): List {
    val destination = mutableListOf()
    for (element in this) {
    if (element is T) {
    destination.add(element)
    }
    }
    return destination
    }
    inline reified
    ["one"]

    View full-size slide

  20. Inlining class reference
    val intent = Intent(this, NewActivity::class.java)

    intent.putExtra(params)

    startActivity(intent)
    startActivity("ANSWER" to 42)
    inline fun Context.startActivity(
    vararg params: Pair) {

    val intent = Intent(this, T::class.java)

    intent.putExtra(params)

    startActivity(intent)

    }
    Generated code:

    View full-size slide

  21. Collections
    vs
    Sequences

    View full-size slide

  22. Extensions on collections are
    inlined
    inline fun Iterable.filter(predicate: (T) -> Boolean): List
    inline fun Iterable.map(transform: (T) -> R): List
    inline fun Iterable.any(predicate: (T) -> Boolean): Boolean
    inline fun Iterable.find(predicate: (T) -> Boolean): T?
    inline fun Iterable.groupBy(keySelector: (T) -> K): Map>
    ...

    View full-size slide

  23. Collections vs Sequences
    eager vs lazy
    evaluation

    View full-size slide

  24. Sequence
    interface Sequence {
    operator fun iterator(): Iterator
    }
    val sequence = listOf(1, 2, 3).asSequence()
    for (i in sequence) { … }
    val numbers = generateSequence(0) { it + 1 }
    numbers.take(5).toList() [0, 1, 2, 3, 4]

    View full-size slide

  25. Collections vs Sequences

    View full-size slide

  26. Nothing happens until terminal operation is called

    View full-size slide

  27. Order of operations is important

    View full-size slide

  28. Collections vs Sequences
    intermediate collections are created on chained calls
    vs
    lambdas are not inlined

    View full-size slide

  29. Copyright © 2017
    https://github.com/JetBrains/kotlin-workshop

    View full-size slide