$30 off During Our Annual Pro Sale. View Details »

Idiomatic Kotlin - IntelliJ IDEA Conf 2022

Anton Arhipov
September 30, 2022

Idiomatic Kotlin - IntelliJ IDEA Conf 2022

Anton Arhipov

September 30, 2022
Tweet

More Decks by Anton Arhipov

Other Decks in Programming

Transcript

  1. Idiomatic Kotlin

    View Slide

  2. @antonarhipov

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. Idiomatic - using, containing, or denoting
    expressions that are natural to a native speaker

    View Slide

  7. Idiomatic - using, containing, or denoting
    expressions that are natural to a native speaker
    Commonly
    accepted style

    View Slide

  8. Idiomatic - using, containing, or denoting
    expressions that are natural to a native speaker
    Commonly
    accepted style
    E
    ff
    ective use of
    language features

    View Slide

  9. Top-Level (Extension) Functions

    View Slide

  10. fun main() {


    doSomething()


    }


    fun doSomething() {


    doMoreStuff(
    :
    :
    finishWork)


    }


    fun doMoreStuff(callback: ()
    ->
    Unit) {


    callback()


    }


    fun finishWork() {


    TODO("Not implemented yet")


    }

    View Slide

  11. fun main() {


    doSomething()


    }


    fun doSomething() {


    doMoreStuff(
    :
    :
    finishWork)


    }


    fun doMoreStuff(callback: ()
    ->
    Unit) {


    callback()


    }


    fun finishWork() {


    TODO("Not implemented yet")


    }

    View Slide

  12. fun main() {


    doSomething()


    }


    fun doSomething() {


    doMoreStuff(
    :
    :
    finishWork)


    }


    fun doMoreStuff(callback: ()
    ->
    Unit) {


    callback()


    }


    fun finishWork() {


    TODO("Not implemented yet")


    }

    View Slide

  13. fun main() {


    doSomething()


    }


    fun doSomething() {


    doMoreStuff(
    :
    :
    finishWork)


    }


    fun doMoreStuff(callback: ()
    ->
    Unit) {


    callback()


    }


    fun finishWork() {


    TODO("Not implemented yet")


    }
    Just functions,


    no classes!

    View Slide

  14. Don’t create classes


    just to hold functions!

    View Slide

  15. class StringUtils {


    companion object {


    fun isPhoneNumber(s: String) =


    s.length
    =
    =
    7
    &
    &
    s.all { it.isDigit() }


    }


    }


    View Slide

  16. class StringUtils {


    companion object {


    fun isPhoneNumber(s: String) =


    s.length
    =
    =
    7
    &
    &
    s.all { it.isDigit() }


    }


    }


    object StringUtils {


    fun isPhoneNumber(s: String) =


    s.length
    =
    =
    7
    &
    &
    s.all { it.isDigit() }


    }


    View Slide

  17. class StringUtils {


    companion object {


    fun isPhoneNumber(s: String) =


    s.length
    =
    =
    7
    &
    &
    s.all { it.isDigit() }


    }


    }


    object StringUtils {


    fun isPhoneNumber(s: String) =


    s.length
    =
    =
    7
    &
    &
    s.all { it.isDigit() }


    }


    fun isPhoneNumber(s: String) =


    s.length
    ==
    7
    &&
    s.all { it.isDigit() }


    View Slide

  18. class StringUtils {


    companion object {


    fun isPhoneNumber(s: String) =


    s.length
    =
    =
    7
    &
    &
    s.all { it.isDigit() }


    }


    }


    object StringUtils {


    fun isPhoneNumber(s: String) =


    s.length
    =
    =
    7
    &
    &
    s.all { it.isDigit() }


    }


    fun isPhoneNumber(s: String) =


    s.length
    ==
    7
    &&
    s.all { it.isDigit() }


    fun String.isPhoneNumber() =


    length
    ==
    7
    &&
    all { it.isDigit() }

    View Slide

  19. Extension or a member?
    https://kotlinlang.org/docs/coding-conventions.html#extension-functions
    Use extension functions liberally


    Restrict the visibility to minimize API pollution


    As necessary, use local extension functions, member
    extension functions, or top-level extension functions with
    private visibility

    View Slide

  20. +

    View Slide

  21. fun findMessageById(id: String) =


    db.query(


    "select * from messages where id = ?",


    RowMapper { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    },


    id


    )
    val db: JdbcTemplate =
    ...

    View Slide

  22. fun findMessageById(id: String) =


    db.query(


    "select * from messages where id = ?",


    RowMapper { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    },


    id


    )
    @Override


    public List query(String sql, RowMapper rowMapper, @Nullable Object
    .
    ..
    args)


    throws DataAccessException {


    return result(query(sql, args, new RowMapperResultSetExtractor
    <
    >
    (rowMapper)));


    }


    View Slide

  23. fun findMessageById(id: String) =


    db.query(


    "select * from messages where id = ?",


    RowMapper { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    },


    id


    )

    View Slide

  24. fun findMessageById(id: String) =


    db.query(


    "select * from messages where id = ?",


    RowMapper { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    },


    id


    )

    View Slide

  25. fun findMessageById(id: String) =


    db.query(


    "select * from messages where id = ?",


    id,


    RowMapper { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    }


    )
    vararg

    View Slide

  26. fun findMessageById(id: String) =


    db.query(


    "select * from messages where id = ?",


    id,


    { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    }


    )
    SAM conversion

    View Slide

  27. fun findMessageById(id: String) =


    db.query(


    "select * from messages where id = ?",


    id)


    { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    }


    Trailing lambda parameter

    View Slide

  28. fun findMessageById(id: String) =


    db.query("select * from messages where id = ?", id ) { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    }

    View Slide

  29. fun findMessageById(id: String) =


    db.query("select * from messages where id = ?", id ) { rs, _
    ->

    Message(rs.getString("id"), rs.getString("text"))


    }
    fun JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet, Int)
    ->
    T): List =


    query(sql, RowMapper { rs, i
    ->
    function(rs, i) }, *args)
    Extension function!

    View Slide

  30. Summary


    Functions, Extensions,
    Trailing lambda,Varargs,
    SAM conversion

    View Slide

  31. Scope functions

    View Slide

  32. View Slide

  33. val dataSource = BasicDataSource()
    dataSource.driverClassName = "com.mysql.jdbc.Driver"
    dataSource.url = "jdbc:mysql://domain:3309/db"
    dataSource.username = "username"
    dataSource.password = "password"
    dataSource.maxTotal = 40
    dataSource.maxIdle = 40
    dataSource.minIdle = 4

    View Slide

  34. val dataSource = BasicDataSource()
    dataSource.driverClassName = "com.mysql.jdbc.Driver"
    dataSource.url = "jdbc:mysql://domain:3309/db"
    dataSource.username = "username"
    dataSource.password = "password"
    dataSource.maxTotal = 40
    dataSource.maxIdle = 40
    dataSource.minIdle = 4
    val dataSource = BasicDataSource().apply {
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://domain:3309/db"
    username = "username"
    password = "password"
    maxTotal = 40
    maxIdle = 40
    minIdle = 4
    }

    View Slide

  35. val dataSource = BasicDataSource().apply {
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://domain:3309/db"
    username = "username"
    password = "password"
    maxTotal = 40
    maxIdle = 40
    minIdle = 4
    }
    public inline fun T.apply(block: T.()
    ->
    Unit): T {


    block()


    return this


    }


    View Slide

  36. val dataSource = BasicDataSource().apply {
    driverClassName = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://domain:3309/db"
    username = "username"
    password = "password"
    maxTotal = 40
    maxIdle = 40
    minIdle = 4
    }
    public inline fun T.apply(block: T.()
    ->
    Unit): T {


    block()


    return this


    }


    Lambda with receiver

    View Slide

  37. ?.let
    val order = retrieveOrder()


    if (order
    !=
    null){


    processCustomer(order.customer)


    }

    View Slide

  38. val order = retrieveOrder()


    if (order
    !=
    null){


    processCustomer(order.customer)


    }
    retrieveOrder()
    ?.
    let {


    processCustomer(it.customer)


    }
    retrieveOrder()
    ?.
    customer
    ?.
    let {
    ::
    processCustomer }
    or
    ?.let

    View Slide

  39. View Slide

  40. fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs() }

    View Slide

  41. fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs() }
    Don’t overuse the scope functions!

    View Slide

  42. fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs() }
    fun makeDir(path: String) : File {


    val file = File(path)


    file.mkdirs()


    return file


    }


    Don’t overuse the scope functions!
    This is simpler!

    View Slide

  43. fun makeDir(path: String) = path.let { File(it) }.also { it.mkdirs() }
    fun makeDir(path: String) : File {


    val file = File(path)


    file.mkdirs()


    return file


    }


    Don’t overuse the scope functions!
    This is simpler!
    fun makeDir(path: String) = File(path).also { it.mkdirs() }
    OK, this one is actually
    fi
    ne :)

    View Slide

  44. Default argument values and named parameters

    View Slide

  45. fun find(name: String){


    find(name, true)


    }


    fun find(name: String, recursive: Boolean){


    }
    Function


    overloading

    View Slide

  46. fun find(name: String){


    find(name, true)


    }


    fun find(name: String, recursive: Boolean){


    }
    fun find(name: String, recursive: Boolean = true){


    }
    Default
    argument value
    Function


    overloading

    View Slide

  47. fun find(name: String){


    find(name, true)


    }


    fun find(name: String, recursive: Boolean){


    }
    fun find(name: String, recursive: Boolean = true){


    }
    fun main() {


    find("myfile.txt")


    }
    Default
    argument value
    Function


    overloading

    View Slide

  48. class Figure(


    val width: Int = 1,


    val height: Int = 1,


    val depth: Int = 1,


    color: Color = Color.BLACK,


    description: String = "This is a 3d figure",


    )
    Figure(Color.RED, "Red figure")

    View Slide

  49. class Figure(


    val width: Int = 1,


    val height: Int = 1,


    val depth: Int = 1,


    color: Color = Color.BLACK,


    description: String = "This is a 3d figure",


    )
    Figure(Color.RED, "Red figure") Compilation error

    View Slide

  50. class Figure(


    val width: Int = 1,


    val height: Int = 1,


    val depth: Int = 1,


    color: Color = Color.BLACK,


    description: String = "This is a 3d figure",


    )
    Figure(color = Color.RED, description = "Red figure")

    View Slide

  51. Default argument values diminish the need


    for overloading in most cases.

    View Slide

  52. Default argument values diminish the need


    for overloading in most cases.
    Named parameters is a necessary tool for


    working with default argument values

    View Slide

  53. Expressions
    if
    when
    try

    View Slide

  54. fun adjustSpeed(weather: Weather): Drive {


    val result: Drive


    if (weather is Rainy) {


    result = Safe()


    } else {


    result = Calm()


    }




    return result


    }


    View Slide

  55. fun adjustSpeed(weather: Weather): Drive {


    val result: Drive


    if (weather is Rainy) {


    result = Safe()


    } else {


    result = Calm()


    }




    return result


    }


    View Slide

  56. fun adjustSpeed(weather: Weather): Drive {




    val result: Drive = if (weather is Rainy) {


    Safe()


    } else {


    Calm()


    }


    return result


    }


    View Slide

  57. fun adjustSpeed(weather: Weather): Drive {




    val result: Drive = if (weather is Rainy) {


    Safe()


    } else {


    Calm()


    }


    return result


    }


    View Slide

  58. fun adjustSpeed(weather: Weather): Drive {




    return if (weather is Rainy) {


    Safe()


    } else {


    Calm()


    }


    }


    View Slide

  59. fun adjustSpeed(weather: Weather): Drive {




    return if (weather is Rainy) {


    Safe()


    } else {


    Calm()


    }


    }


    View Slide

  60. fun adjustSpeed(weather: Weather): Drive = if (weather is Rainy) {


    Safe()


    } else {


    Calm()


    }


    View Slide

  61. fun adjustSpeed(weather: Weather): Drive = if (weather is Rainy) {


    Safe()


    } else {


    Calm()


    }


    View Slide

  62. fun adjustSpeed(weather: Weather) = if (weather is Rainy) {


    Safe()


    } else {


    Calm()


    }


    View Slide

  63. fun adjustSpeed(weather: Weather) = if (weather is Rainy) {


    Safe()


    } else {


    Calm()


    }


    View Slide

  64. fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()


    Is it concise?
    Sure!

    View Slide

  65. fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()


    Is it readable?
    It depends!

    View Slide

  66. fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()


    What does the function
    return?

    View Slide

  67. fun adjustSpeed(weather: Weather): Drive =
    ...

    fun adjustSpeed(weather: Weather) =
    ...
    For public API, keep the
    return type in the
    signature
    For private API it is
    generally OK to use type
    inference

    View Slide

  68. fun adjustSpeed(weather: Weather) = if (weather is Rainy) Safe() else Calm()


    View Slide

  69. abstract class Weather


    class Sunny : Weather()


    class Rainy : Weather()


    fun adjustSpeed(weather: Weather) = when (weather) {


    is Rainy
    ->
    Safe()


    else
    ->
    Calm()


    }


    View Slide

  70. sealed class Weather


    class Sunny : Weather()


    class Rainy : Weather()


    fun adjustSpeed(weather: Weather) = when (weather) {


    is Rainy
    ->
    Safe()


    / /
    else
    ->
    Calm()


    }


    View Slide

  71. sealed class Weather


    class Sunny : Weather()


    class Rainy : Weather()


    fun adjustSpeed(weather: Weather) = when (weather) {


    is Rainy
    ->
    Safe()


    / /
    else
    ->
    Calm()


    }


    View Slide

  72. sealed class Weather


    class Sunny : Weather()


    class Rainy : Weather()


    fun adjustSpeed(weather: Weather) = when (weather) {


    is Rainy
    ->
    Safe()


    is Sunny
    ->
    TODO()


    }


    View Slide

  73. sealed class Weather


    class Sunny : Weather()


    class Rainy : Weather()


    fun adjustSpeed(weather: Weather) = when (weather) {


    is Rainy
    ->
    Safe()


    is Sunny
    ->
    TODO()


    }


    Use expressions!


    Use when as expression body


    Use sealed classes with when

    View Slide

  74. Null-safety

    View Slide

  75. class Nullable {


    fun someFunction(){}


    }


    fun createNullable(): Nullable? = null


    fun main() {


    val n: Nullable? = createNullable()




    n.someFunction()


    }


    View Slide

  76. class Nullable {


    fun someFunction(){}


    }


    fun createNullable(): Nullable? = null


    fun main() {


    val n: Nullable? = createNullable()




    n.someFunction()


    }


    View Slide

  77. Consider using null-safe call
    val order = retrieveOrder()


    if (order
    ==
    null
    ||
    order.customer
    = =
    null
    ||
    order.customer.address
    ==
    null){


    throw IllegalArgumentException("Invalid Order")


    }


    val city = order.customer.address.city


    View Slide

  78. Consider using null-safe call
    val order = retrieveOrder()


    val city = order
    ?.
    customer
    ? .
    address
    ?.
    city


    View Slide

  79. val order = retrieveOrder()


    val city = order
    ?.
    customer
    ? .
    address
    ?.
    city


    ?:
    throw IllegalArgumentException("Invalid Order")


    Consider using null-safe call

    View Slide

  80. Avoid not-null assertions
    !
    !
    val order = retrieveOrder()


    val city = order
    !!
    .customer
    !!
    .address
    !!
    .city


    “You may notice that the double exclamation mark looks a bit rude:
    it’s almost like you’re yelling at the compiler. This is intentional.” - Kotlin in Action

    View Slide

  81. Avoid not-null assertions
    !
    !
    class MyTest {


    class State(val data: String)


    private var state: State? = null


    @BeforeEach


    fun setup() {


    state = State("abc")


    }


    @Test


    fun foo() {


    assertEquals("abc", state
    !!
    .data)


    }


    }

    View Slide

  82. Avoid not-null assertions
    !
    !
    class MyTest {


    class State(val data: String)


    private var state: State? = null


    @BeforeEach


    fun setup() {


    state = State("abc")


    }


    @Test


    fun foo() {


    assertEquals("abc", state
    !!
    .data)


    }


    }
    class MyTest {


    class State(val data: String)


    private lateinit var state: State


    @BeforeEach


    fun setup() {


    state = State("abc")


    }


    @Test


    fun foo() {


    assertEquals("abc", state.data)


    }


    }
    - use lateinit

    View Slide

  83. View Slide

  84. Use elvis operator
    class Person(val name: String?, val age: Int?)


    val p = retrievePerson()
    ?:
    Person()


    View Slide

  85. Use elvis operator as return and throw
    class Person(val name: String?, val age: Int?)


    fun processPerson(person: Person) {


    val name = person.name


    if (name
    = =
    null)


    throw IllegalArgumentException("Named required")


    val age = person.age


    if (age
    ==
    null) return


    println("$name: $age")


    }


    View Slide

  86. Use elvis operator as return and throw
    class Person(val name: String?, val age: Int?)


    fun processPerson(person: Person) {


    val name = person.name


    if (name
    = =
    null)


    throw IllegalArgumentException("Named required")


    val age = person.age


    if (age
    ==
    null) return


    println("$name: $age")


    }


    View Slide

  87. Use elvis operator as return and throw
    class Person(val name: String?, val age: Int?)


    fun processPerson(person: Person) {


    val name = person.name


    if (name
    = =
    null)


    throw IllegalArgumentException("Named required")


    val age = person.age


    if (age
    ==
    null) return


    println("$name: $age")


    }


    View Slide

  88. Use elvis operator as return and throw
    class Person(val name: String?, val age: Int?)


    fun processPerson(person: Person) {


    val name = person.name
    ? :

    throw IllegalArgumentException("Named required")


    val age = person.age
    ?:
    return


    println("$name: $age")


    }


    View Slide

  89. Consider using safe cast for type checking
    override fun equals(other: Any?) : Boolean {


    val command = other as Command


    return command.id
    ==
    id


    }

    View Slide

  90. Consider using safe cast for type checking
    override fun equals(other: Any?) : Boolean {


    val command = other as Command


    return command.id
    ==
    id


    }
    override fun equals(other: Any?) : Boolean {


    return (other as? Command)
    ?.
    id
    ==
    id


    }

    View Slide

  91. Ranges

    View Slide

  92. Use range checks instead of comparison pairs
    fun isLatinUppercase(c: Char) =


    c
    >
    =
    'A'
    &&
    c
    < =
    'Z'


    View Slide

  93. fun isLatinUppercase(c: Char) =


    c
    >
    =
    'A'
    &&
    c
    < =
    'Z'


    Use range checks instead of comparison pairs

    View Slide

  94. Use range checks instead of comparison pairs
    fun isLatinUppercase(c: Char) =


    c in 'A'
    .
    .
    'Z'


    View Slide

  95. Use range checks instead of comparison pairs
    fun isLatinUppercase(c: Char) =


    c in 'A'
    .
    .
    'Z'


    View Slide

  96. class Version(val major: Int, val minor: Int): Comparable {


    override fun compareTo(other: Version): Int {


    if (this.major
    !=
    other.major) {


    return this.major - other.major


    }


    return this.minor - other.minor


    }


    }


    fun main() {


    val versionRange = Version(1, 11)
    .
    .
    Version(1, 30)


    println(Version(0, 9) in versionRange)


    println(Version(1, 20) in versionRange)


    }


    Comparable range

    View Slide

  97. class Version(val major: Int, val minor: Int): Comparable {


    override fun compareTo(other: Version): Int {


    if (this.major
    !=
    other.major) {


    return this.major - other.major


    }


    return this.minor - other.minor


    }


    }


    fun main() {


    val versionRange = Version(1, 11)
    .
    .
    Version(1, 30)


    println(Version(0, 9) in versionRange)


    println(Version(1, 20) in versionRange)


    }


    Comparable range
    public operator fun >
    >
    T.rangeTo(that: T): ClosedRange


    = ComparableRange(this, that)

    View Slide

  98. class Version(val major: Int, val minor: Int): Comparable {


    override fun compareTo(other: Version): Int {


    if (this.major
    !=
    other.major) {


    return this.major - other.major


    }


    return this.minor - other.minor


    }


    }


    fun main() {


    val versionRange = Version(1, 11)
    .
    .
    Version(1, 30)


    println(Version(0, 9) in versionRange)


    println(Version(1, 20) in versionRange)


    }


    Comparable range
    public operator fun >
    >
    T.rangeTo(that: T): ClosedRange


    = ComparableRange(this, that)

    View Slide

  99. Ranges in loops
    fun main(args: Array) {




    for (i in 0
    ..
    args.size - 1) {


    println("$i: ${args[i]}")


    }




    }


    View Slide

  100. Ranges in loops
    fun main(args: Array) {




    for (i in 0
    ..
    args.size - 1) {


    println("$i: ${args[i]}")


    }




    }


    View Slide

  101. Ranges in loops
    fun main(args: Array) {




    for (i in 0
    ..
    args.size - 1) {


    println("$i: ${args[i]}")


    }




    }


    for (i in 0 until args.size) {


    println("$i: ${args[i]}")


    }

    View Slide

  102. Ranges in loops
    fun main(args: Array) {




    for (i in 0
    ..
    args.size - 1) {


    println("$i: ${args[i]}")


    }




    }


    for (i in 0 until args.size) {


    println("$i: ${args[i]}")


    }
    for (i in args.indices) {


    println("$i: ${args[i]}")


    }

    View Slide

  103. Ranges in loops
    fun main(args: Array) {




    for (i in 0
    ..
    args.size - 1) {


    println("$i: ${args[i]}")


    }




    }


    for (i in 0 until args.size) {


    println("$i: ${args[i]}")


    }
    for (i in args.indices) {


    println("$i: ${args[i]}")


    }
    for ((i, arg) in args.withIndex()) {


    println("$i: $arg")


    }

    View Slide

  104. View Slide

  105. View Slide

  106. View Slide

  107. Standard library

    View Slide

  108. abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    Group 1:
    Group 2:
    Count the total of characters that are present


    on each line in every group of strings

    View Slide

  109. abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    Group 1:
    Group 2:
    Count the total of characters that are present


    on each line in every group of strings

    View Slide

  110. abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    Group 1:
    Group 2:
    {d}


    {xy}


    Count the total of characters that are present


    on each line in every group of strings

    View Slide

  111. abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    Group 1:
    Group 2:
    {d}


    {xy}


    count = 1


    count = 2


    Count the total of characters that are present


    on each line in every group of strings

    View Slide

  112. abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    Group 1:
    Group 2:
    {d}


    {xy}


    count = 1


    count = 2


    Total = 3
    Count the total of characters that are present


    on each line in every group of strings

    View Slide

  113. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()

    View Slide

  114. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()
    val groups: List = input.split("\n\n")

    View Slide

  115. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()
    val groups: List = input.split("\n\n")

    View Slide

  116. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()
    val groups: List = input.split("\n\n")
    var total = 0


    for (group in groups) {


    val listOfSets: List>>

    = group.split("\n").map(String
    ::
    toSet)


    var result = listOfSets.first()


    for (set in listOfSets) {


    result = result intersect set


    }


    total += result.count()


    }


    View Slide

  117. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()
    val groups: List = input.split("\n\n")
    var total = 0


    for (group in groups) {


    val listOfSets: List>>

    = group.split("\n").map(String
    ::
    toSet)


    var result = listOfSets.first()


    for (set in listOfSets) {


    result = result intersect set


    }


    total += result.count()


    }


    View Slide

  118. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()
    val groups: List = input.split("\n\n")
    var total = 0


    for (group in groups) {


    val listOfSets: List>>

    = group.split("\n").map(String
    ::
    toSet)


    var result = listOfSets.first()


    for (set in listOfSets) {


    result = result intersect set


    }


    total += result.count()


    }


    View Slide

  119. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()
    val groups: List = input.split("\n\n")
    var total = 0


    for (group in groups) {


    val listOfSets: List>>

    = group.split("\n").map(String
    ::
    toSet)


    var result = listOfSets.first()


    for (set in listOfSets) {


    result = result intersect set


    }


    total += result.count()


    }


    View Slide

  120. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()
    val groups: List = input.split("\n\n")
    var total = 0


    for (group in groups) {


    val listOfSets: List>>

    = group.split("\n").map(String
    ::
    toSet)


    var result = listOfSets.first()


    for (set in listOfSets) {


    result = result intersect set


    }


    total += result.count()


    }


    Transforming data

    View Slide

  121. val input = """


    abcd


    bcde


    cdef


    defg


    xya


    xyb


    xyc


    """.trimIndent()
    val groups: List = input.split("\n\n")
    var total = 0


    for (group in groups) {


    val listOfSets: List>>

    = group.split("\n").map(String
    ::
    toSet)


    var result = listOfSets.first()


    for (set in listOfSets) {


    result = result intersect set


    }


    total += result.count()


    }


    Calculating the result
    Transforming data

    View Slide

  122. val groups: List = input.split("\n\n")

    View Slide

  123. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    Transforming data

    View Slide

  124. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    Transforming data

    View Slide

  125. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    Transforming data

    View Slide

  126. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    Transforming data

    View Slide

  127. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    Transforming data

    View Slide

  128. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    val step2 = step1.sumOf { it.reduce { a, b
    - >
    a intersect b }.count() }
    Transforming data
    Calculating the result

    View Slide

  129. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    val step2 = step1.sumOf { it.reduce { a, b
    - >
    a intersect b }.count() }
    Calculating the result
    Transforming data

    View Slide

  130. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    val step2 = step1.sumOf { it.reduce { a, b
    - >
    a intersect b }.count() }
    Calculating the result
    Transforming data

    View Slide

  131. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    val step2 = step1.sumOf { it.reduce { a, b
    - >
    a intersect b }.count() }
    Calculating the result
    Transforming data

    View Slide

  132. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    val step2 = step1.sumOf { it.reduce { a, b
    - >
    a intersect b }.count() }
    Calculating the result
    Transforming data

    View Slide

  133. val groups: List = input.split("\n\n")
    //
    List>>>

    val step1 = groups.map { it.split("\n").map(String
    ::
    toSet) }
    val step2 = step1.sumOf { it.reduce { a, b
    - >
    a intersect b }.count() }
    groups.map { group
    ->

    group.split(nl).map(String
    ::
    toSet)


    }.sumOf { answerSets
    ->

    answerSets.reduce { a, b
    ->
    a intersect b }.count()


    }


    Calculating the result
    Transforming data

    View Slide

  134. View Slide

  135. View Slide

  136. kotlin {


    twitter = "@kotlin"


    youtube = "youtube.com/kotlin"


    slack = "slack.kotl.in"


    }


    me {


    name = "Anton Arhipov"


    twitter = "@antonarhipov"


    slides = "speakerdeck.com/antonarhipov"


    }


    View Slide