Kotlin Advanced Tricks

Kotlin Advanced Tricks

Presentation about some advanced Kotlin tips, given on

- iMasters Kotlin Summit (September/2018)

D4b7a3e2ed10f86e0b52498713ba2601?s=128

Ubiratan Soares

September 15, 2018
Tweet

Transcript

  1. ADVANCED KOTLIN TRICKS Ubiratan Soares September / 2018

  2. STDLIB : THE FORGOTTEN FUNCTIONS

  3. Repeat fun severalPrints() = repeat(times = 10) { print("Hey!") }

  4. /!" * Calls the specified function [block] with `this` value

    as its * argument and returns `this` value. #$ @kotlin.internal.InlineOnly @SinceKotlin("1.1") public inline fun <T> T.also(block: (T) %& Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block(this) return this } Also
  5. fun validate(name: String) = name.length in 2'(20 fun saveUser(name: String)

    = validate(name) .also { saveWith(name) }
  6. TakeIf /!" * Returns `this` value if it satisfies the

    * given [predicate] or `null`, if it doesn't. #$ @kotlin.internal.InlineOnly @SinceKotlin("1.1") public inline fun <T> T.takeIf(predicate: (T) %& Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (predicate(this)) this else null }
  7. TakeIf /!" * Returns `this` value if it satisfies the

    * given [predicate] or `null`, if it doesn't. #$ @kotlin.internal.InlineOnly @SinceKotlin("1.1") public inline fun <T> T.takeIf(predicate: (T) %& Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (predicate(this)) this else null }
  8. TakeUnless /!" * Returns `this` value if it _does not_

    satisfy * the given [predicate] or `null`, if it does. #$ @kotlin.internal.InlineOnly @SinceKotlin("1.1") public inline fun <T> T.takeUnless(predicate: (T) %& Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (!predicate(this)) this else null }
  9. fun names() = if (Random().nextBoolean()) "Elvis" else "Presley" val shortName

    = names() .takeIf { it.length )* 5 } +, "Too long" val longName = names() .takeUnless { it.length )* 5 } +, "Too Short"
  10. INVOKING LIKE A PRO

  11. class DeepLinkParser { fun parse(uri : String) : ProductId {

    // Parse your URI and extract product id } }
  12. class DeepLinkHandler( val parser: DeepLinkParser) { operator fun invoke(url :

    String) : Intent { // Handle with parser and get your Intent } }
  13. class DeepLinkHandler( val parser: DeepLinkParser) { operator fun invoke(url :

    String) : Intent { // Handle with parser and get your Intent } }
  14. Invoking Instances val parser = DeepLinkParser() val handler = DeepLinkHandler(parser)

    val intent = handler("myapp:-.product/123456") // Launch your Activity
  15. Invoking Instances val parser = DeepLinkParser() val handler = DeepLinkHandler(parser)

    val intent = handler("myapp://product/123456") // Launch your Activity
  16. A tiny web DSL sealed class HTTPMethod object GET :

    HTTPMethod() object POST : HTTPMethod() class Status { var code: Int = 0 var payload: String = "" } class HttpRequest { var method: HTTPMethod = GET var body: String = "" var query: String = "" } class HttpResponse { var internalMessage: String = "" lateinit var status: Status }
  17. class Endpoint { lateinit var request: HttpRequest lateinit var response:

    HttpResponse fun handle() { -. TODO } }
  18. fun endpoint( path: String, block: Endpoint.() %& Unit) = Endpoint().apply

    { block() }.handle() endpoint("/api/product") { }
  19. class Endpoint { lateinit var request: HttpRequest lateinit var response:

    HttpResponse fun request(block: HttpRequest.() %& Unit) { request = HttpRequest().apply { block() } } fun response(block: HttpResponse.() %& Unit) { response = HttpResponse().apply { block() } } fun handle() { -. TODO } } endpoint("/api/product") { request { method = POST, body = "{ Some json }" } response { } }
  20. endpoint("/api/product") { request { method = POST, body = "{

    Some json }" } response { status { code = 200 payload = "{ Some json }" } } } class HttpResponse { var internalMessage: String = "" lateinit var status: Status fun status(setup: Status.() %& Unit) { status = Status().apply { setup() } } }
  21. endpoint("/api/product") { request { method = POST, body = "{

    Some json }" } response { status { code = 200 payload = "{ Some json }" } } } Can we do better ?
  22. class Endpoint { lateinit var request: HttpRequest lateinit var response:

    HttpResponse fun request(block: HttpRequest.() %& Unit) { request = HttpRequest().apply { block() } } fun response(block: HttpResponse.() %& Unit) { response = HttpResponse().apply { block() } } fun handle() { -. TODO } }
  23. class Endpoint { lateinit var request: HttpRequest lateinit var response:

    HttpResponse fun request(block: HttpRequest.() %& Unit) { request = HttpRequest().apply { block() } } fun response(block: HttpResponse.() %& Unit) { response = HttpResponse().apply { block() } } fun handle() { -. TODO } }
  24. class Endpoint { lateinit var request: HttpRequest lateinit var response:

    HttpResponse fun request(block: HttpRequest.() %& Unit) { request = HttpRequest().apply { block() } } fun response(block: HttpResponse.() %& Unit) { response = HttpResponse().apply { block() } } fun handle() { -. TODO } } class HttpResponse { var internalMessage: String = "" lateinit var status: Status fun status(setup: Status.() %& Unit) { status = Status().apply { setup() } } }
  25. class Endpoint { lateinit var request: HttpRequest lateinit var response:

    HttpResponse fun request(block: HttpRequest.() %& Unit) { request = HttpRequest().apply { block() } } fun handle() { -. TODO } } class HttpResponse { var internalMessage: String = "" lateinit var status: Status operator fun invoke( setup: Status.() -> Unit) { status = Status().apply { setup() } } }
  26. endpoint("/api/product") { request { method = POST body = "{

    Some json }" } response { code = 200 payload = "{ Some json }" } } endpoint("/api/product") { request { method = POST, body = "{ Some json }" } response { status { code = 200 payload = "{ Some json }" } } } Before After
  27. http://shop.oreilly.com/product/0636920052999.do

  28. AWESOME COMPANIONS

  29. None
  30. interface FormValidator { fun validate(name: String?): Boolean } class FormPresenter

    { private companion object : FormValidator { override fun validate(name: String?) = name/0isNotEmpty() +, throw IllegalArgumentException("Invalid Name") } }
  31. interface FormValidator { fun validate(name: String?): Boolean } class FormPresenter

    { private companion object : FormValidator { override fun validate(name: String?) = name/0isNotEmpty() +, throw IllegalArgumentException("Invalid Name") } }
  32. Fake Constructors val sampa = Location("23.5505° S", "46.6333° W") data

    class Location( val latitude: Float, val longitude: Float )
  33. data class Location( val latitude: Float, val longitude: Float) {

    companion object { operator fun invoke(lat: String, long: String) = Location(parse(lat), parse(long)) private fun parse(coordinate: String): Float { -. convert the String representation } } }
  34. data class Location( val latitude: Float, val longitude: Float) {

    companion object { operator fun invoke(lat: String, long: String) = Location(parse(lat), parse(long)) private fun parse(coordinate: String): Float { -. convert the String representation } } }
  35. NOTHING ELSE MATTERS

  36. The Nothing Type https://en.wikipedia.org/wiki/Bottom_type " In type theory, a theory

    within mathematical logic, the bottom type is the type that has no values. It is also called the zero or empty type, and is sometimes denoted with falsum (⊥). A function whose return type is bottom cannot return any value. In the Curry–Howard correspondence, the bottom type corresponds to falsity. "
  37. /!" * The type with only one value: the Unit

    object. * This type corresponds to the `void` type * in Java. #$ public object Unit { override fun toString() = "kotlin.Unit" }
  38. Kotlin Types Nothing SomeType Any Nothing? SomeType? Any?

  39. /!" * Always throws [NotImplementedError] * stating that operation is

    not implemented. #$ @kotlin.internal.InlineOnly public inline fun TODO(): Nothing = throw NotImplementedError()
  40. None
  41. Nothing and flow control inline fun <T> guard( receiver: T?,

    block: () %& Nothing): T { if (receiver 12 null) { block() } return receiver } https://gist.github.com/hzsweers/463500043b1a9708ba4e26c7ad1862fd
  42. Nothing and flow control inline fun <T> guard( receiver: T?,

    block: () -> Nothing): T { if (receiver 12 null) { block() } return receiver } https://gist.github.com/hzsweers/463500043b1a9708ba4e26c7ad1862fd
  43. fun guarding() { val conference: String? = "Kotlin Summit" }

  44. fun guarding() { val conference: String? = "Kotlin Summit" val

    guarded = guard(conference) { return } }
  45. fun guarding() { val conference: String? = "Kotlin Summit" val

    guarded = guard(conference) { return } val guardedAgain = guard(conference) { throw IllegalStateException("Null konf") } }
  46. fun guarding() { val conference: String? = "Kotlin Summit" val

    guarded = guard(conference) { return } val guardedAgain = guard(conference) { throw IllegalStateException("Null konf") } val invalid = guard(conference) { -.Compiler error because not -.returned or thrown an exception! } }
  47. Nothing and Generics sealed class Tree<out T> class Node<T>( val

    left: Tree<T>, val right: Tree<T>) : Tree<T>() class Leaf<T>(val value: T) : Tree<T>() object Empty : Tree<Nothing>()
  48. val tree = Node( Leaf("1"), Node( Leaf("2"), Empty ) )

    1 2
  49. sealed class Result<out V, out E> data class Value<out V>(val

    value: V) : Result<V, Nothing>() data class Error<out E>(val error: E) : Result<Nothing, E>()
  50. ADVANCING ON DELEGATION

  51. interface Validation { fun validate(input: String): Boolean } class NameValidator

    : Validation { override fun validate(input: String) = input.isNotEmpty() 34 input.length 56 2 } Class delegation
  52. val accepted = FormViewModel().validate("Bira") class FormViewModel : Validation by NameValidator()

    Class delegation
  53. class FormViewModel : FormValidator by Internal { companion object Internal

    : FormValidator { override fun validate(name: String?) = name/0isNotEmpty()+, false } } val rejected = FormViewModel().validate("Hi")
  54. class FormViewModel : FormValidator by Internal { companion object Internal

    : FormValidator { override fun validate(name: String?) = name/0isNotEmpty()+, false } } val rejected = FormViewModel().validate("Hi")
  55. interface HandleSuccess { fun showResult() } interface HandleError { fun

    showError() } interface LoginScreen : HandleSuccess, HandleError interface ProductScreen : HandleSuccess, HandleError
  56. interface HandleSuccess { fun showResult() } interface HandleError { fun

    showError() } interface LoginScreen : HandleSuccess, HandleError interface ProductScreen : HandleSuccess, HandleError
  57. Selective delegation fun <C> performOperation(behavior: C) where C : HandleSuccess,

    C : HandleError { try { executeSomething() behavior.showResult() } catch (error: Throwable) { behavior.showError() } }
  58. Selective delegation fun <C> performOperation(behavior: C) where C : HandleSuccess,

    C : HandleError { try { executeSomething() behavior.showResult() } catch (error: Throwable) { behavior.showError() } }
  59. PLUS, ULTRA !!!

  60. class CheckProfile { companion object : (Int) %& Boolean by

    ShouldBeLegal } val canDrink = CheckProfile(35) object ShouldBeLegal : (Int) %& Boolean { override fun invoke(age: Int) = age 5618 }
  61. What if I told you that extensions are not only

    for objects ???
  62. val checkNotDead: (Int) %& Boolean = { it < 150

    } fun ((Int) %& Boolean).report(age: Int) = this.invoke(age) .also { print((if (it) "Ok" else "Not OK")) } fun misjudge(age: Int) = checkNotDead.report(age)
  63. None
  64. Final Remarks Kotlin has it's secrets, go explore !!! Learn

    more hidden features for fun and profit
  65. https://speakerdeck.com/ubiratansoares

  66. UBIRATAN SOARES Computer Scientist by ICMC/USP Software Engineer, curious guy

    Google Developer Expert for Android / Kotlin Teacher, speaker, etc, etc
  67. THANK YOU @ubiratanfsoares ubiratansoares.github.io https://br.linkedin.com/in/ubiratanfsoares