Slide 1

Slide 1 text

ADVANCED KOTLIN TRICKS Ubiratan Soares September / 2018

Slide 2

Slide 2 text

STDLIB : THE FORGOTTEN FUNCTIONS

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

/!" * 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.also(block: (T) %& Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block(this) return this } Also

Slide 5

Slide 5 text

fun validate(name: String) = name.length in 2'(20 fun saveUser(name: String) = validate(name) .also { saveWith(name) }

Slide 6

Slide 6 text

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.takeIf(predicate: (T) %& Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (predicate(this)) this else null }

Slide 7

Slide 7 text

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.takeIf(predicate: (T) %& Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (predicate(this)) this else null }

Slide 8

Slide 8 text

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.takeUnless(predicate: (T) %& Boolean): T? { contract { callsInPlace(predicate, InvocationKind.EXACTLY_ONCE) } return if (!predicate(this)) this else null }

Slide 9

Slide 9 text

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"

Slide 10

Slide 10 text

INVOKING LIKE A PRO

Slide 11

Slide 11 text

class DeepLinkParser { fun parse(uri : String) : ProductId { // Parse your URI and extract product id } }

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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 }

Slide 17

Slide 17 text

class Endpoint { lateinit var request: HttpRequest lateinit var response: HttpResponse fun handle() { -. TODO } }

Slide 18

Slide 18 text

fun endpoint( path: String, block: Endpoint.() %& Unit) = Endpoint().apply { block() }.handle() endpoint("/api/product") { }

Slide 19

Slide 19 text

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 { } }

Slide 20

Slide 20 text

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() } } }

Slide 21

Slide 21 text

endpoint("/api/product") { request { method = POST, body = "{ Some json }" } response { status { code = 200 payload = "{ Some json }" } } } Can we do better ?

Slide 22

Slide 22 text

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 } }

Slide 23

Slide 23 text

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 } }

Slide 24

Slide 24 text

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() } } }

Slide 25

Slide 25 text

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() } } }

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

http://shop.oreilly.com/product/0636920052999.do

Slide 28

Slide 28 text

AWESOME COMPANIONS

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

Fake Constructors val sampa = Location("23.5505° S", "46.6333° W") data class Location( val latitude: Float, val longitude: Float )

Slide 33

Slide 33 text

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 } } }

Slide 34

Slide 34 text

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 } } }

Slide 35

Slide 35 text

NOTHING ELSE MATTERS

Slide 36

Slide 36 text

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. "

Slide 37

Slide 37 text

/!" * 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" }

Slide 38

Slide 38 text

Kotlin Types Nothing SomeType Any Nothing? SomeType? Any?

Slide 39

Slide 39 text

/!" * Always throws [NotImplementedError] * stating that operation is not implemented. #$ @kotlin.internal.InlineOnly public inline fun TODO(): Nothing = throw NotImplementedError()

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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! } }

Slide 47

Slide 47 text

Nothing and Generics sealed class Tree class Node( val left: Tree, val right: Tree) : Tree() class Leaf(val value: T) : Tree() object Empty : Tree()

Slide 48

Slide 48 text

val tree = Node( Leaf("1"), Node( Leaf("2"), Empty ) ) 1 2

Slide 49

Slide 49 text

sealed class Result data class Value(val value: V) : Result() data class Error(val error: E) : Result()

Slide 50

Slide 50 text

ADVANCING ON DELEGATION

Slide 51

Slide 51 text

interface Validation { fun validate(input: String): Boolean } class NameValidator : Validation { override fun validate(input: String) = input.isNotEmpty() 34 input.length 56 2 } Class delegation

Slide 52

Slide 52 text

val accepted = FormViewModel().validate("Bira") class FormViewModel : Validation by NameValidator() Class delegation

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

PLUS, ULTRA !!!

Slide 60

Slide 60 text

class CheckProfile { companion object : (Int) %& Boolean by ShouldBeLegal } val canDrink = CheckProfile(35) object ShouldBeLegal : (Int) %& Boolean { override fun invoke(age: Int) = age 5618 }

Slide 61

Slide 61 text

What if I told you that extensions are not only for objects ???

Slide 62

Slide 62 text

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)

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

Final Remarks Kotlin has it's secrets, go explore !!! Learn more hidden features for fun and profit

Slide 65

Slide 65 text

https://speakerdeck.com/ubiratansoares

Slide 66

Slide 66 text

UBIRATAN SOARES Computer Scientist by ICMC/USP Software Engineer, curious guy Google Developer Expert for Android / Kotlin Teacher, speaker, etc, etc

Slide 67

Slide 67 text

THANK YOU @ubiratanfsoares ubiratansoares.github.io https://br.linkedin.com/in/ubiratanfsoares