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

State of the functional ecosystem in Kotlin: Mid-2018 checkup

State of the functional ecosystem in Kotlin: Mid-2018 checkup

In this talk we’ll go through several of the new patterns and libraries that the Arrow organization is helping grow in the Kotlin community.

Starting from the perspective of someone without experience in functional languages and idioms, what problems are these functional libraries trying to solve, and how do they compare to existing libraries. Lastly, we’ll go through some of the examples and resources available online for those who work on libraries and infrastructure.

pakoito

June 15, 2018
Tweet

More Decks by pakoito

Other Decks in Programming

Transcript

  1. @pacoworks
    Paco Estevez
    State of the functional
    ecosystem in Kotlin:
    Mid-2018 checkup

    View Slide

  2. @pacoworks
    We’re growing our tools
    We’re improving our ecosystem
    We want all of you to be part of it
    2

    View Slide

  3. @pacoworks
    Where does Arrow fit in your day to day
    code?
    Looking past the basic constructs
    What projects are using Arrow, today
    3

    View Slide

  4. @pacoworks
    Building
    functional
    intuition
    4

    View Slide

  5. @pacoworks
    This is readable
    fun deepUpdate(m: Meetup, f: (Id) -> Id) {
    val l = ArrayList()
    for (talk in m.talks) {
    val newAtt = ArrayList()
    for (attendee in talk.attendees) {
    newAtt += Attendee(id = f(attendee.id))
    }
    l += talk.copy(attendees = newAtt.toList())
    }
    m.copy(talks = list.toList())
    }
    5
    Two mutations, plenty of lines

    View Slide

  6. @pacoworks
    This is readable familiar
    fun deepUpdate(m: Meetup, f: (Id) -> Id) {
    val l = ArrayList()
    for (talk in m.talks) {
    val newAtt = ArrayList()
    for (attendee in talk.attendees) {
    newAtt += Attendee(id = f(attendee.id))
    }
    l += talk.copy(attendees = newAtt.toList())
    }
    m.copy(talks = list.toList())
    }
    6
    Two mutations, plenty of lines

    View Slide

  7. @pacoworks
    This can become familiar
    fun deepUpdate(m: Meetup, f: (Id) -> Id) =
    m.copy(talks = m.talks.map {
    it.attendees.map { it.copy(id = f(it.id)) }
    })
    7

    View Slide

  8. @pacoworks
    This can become familiar
    fun deepUpdate(m: Meetup, f: (Id) -> Id) =
    Meetup.talks.every().attendees.every().id {
    meetup.modify(f)
    }
    8

    View Slide

  9. @pacoworks
    Myths and misconceptions
    No magic: function composition
    Not a complex stream with internal state
    and threads
    Replaced in tests by a function or
    implementing an interface with 1-3
    methods
    9
    Of getter/setter for this example!

    View Slide

  10. @pacoworks
    Most common question
    10

    View Slide

  11. @pacoworks
    How is the performance?
    Runtime performance?
    Compile-time performance?
    Development time performance?
    Debug time performance?
    11
    Same as hand rolling it, except we’ve
    already done it.
    Can be hand-rolled with our
    abstractions, annotation processed, or
    even checked into the repo
    Net perf improvement. LARGE.
    Pure function composition, only the
    input parameters can fail.

    View Slide

  12. @pacoworks
    Tackling the
    ordinary with
    Arrow
    12

    View Slide

  13. @pacoworks
    API design concerns
    I don’t want to write a new class every time
    a function has multiple returns
    My function return can fail, throw, or branch
    out
    I need this function to be lazy or cache its
    results
    Our Android app cannot take one more library!
    13

    View Slide

  14. @pacoworks
    arrow-core
    I don’t want to write a new class every
    time a function has multiple returns
    Tuples
    14
    fun initializeSubsystems(): Tuple2
    Up to 21 elements
    Starter

    View Slide

  15. @pacoworks
    arrow-core
    My function return can fail, throw, or
    branch out
    Either, Option, Try
    15
    fun find(l: List, f: (A) -> Boolean):
    Option>
    Starter

    View Slide

  16. @pacoworks
    arrow-core
    I need this function to be lazy or cache
    its results
    Eval
    16
    fun foldRight(lb: Eval, f: (A, Eval) -> Eval):
    Eval
    Starter

    View Slide

  17. @pacoworks
    arrow-data
    Collections and wrappers
    NonEmptyList, ListK, MapK, SetK…
    Read dependencies, write to logs, carry over state
    Reader, Writer, State
    Compose datatypes
    Transformers
    17
    Starter

    View Slide

  18. @pacoworks
    How do I handle functions
    like data?
    18
    val fIntToInt: (Int) -> Int =
    { p: Int -> p + 1 }
    val fIntToString: (Int) -> String =
    { it.toString() }
    val fIntAndIntToInt: (Int, Int) —> Int =
    { p: Int, q: Int -> p + q }

    View Slide

  19. @pacoworks
    How NOT to handle functions
    like data
    19
    { fIntToInt(fIntToInt(it)) }
    { fIntToString(fIntToInt(it)) }
    { p: Int, q: Int -> fIntToInt(fIntAndIntToInt(p,q)) }
    { p: Int, _: Int -> p + 10 }

    View Slide

  20. @pacoworks
    arrow-syntax
    20
    0 -> fIntToInt andThen fIntToInt // 2
    2 -> fIntToString compose fIntToInt // “3”
    2, 3 -> fIntAndIntToInt andThen fIntToInt // 6
    2 -> fIntAndIntToInt apply 10 // 12
    Tuple2(2, 3) -> fIntAndIntToInt.paired() // 5
    fIntToExpensive.memoize()
    -> is called pipe
    Starter

    View Slide

  21. @pacoworks
    Easing the
    bigger ideas
    21

    View Slide

  22. @pacoworks
    Working with plain data
    Data classes: accessing fields
    Sealed classes: matching with when or fold
    Collections: traversing elements
    Maps: accessing elements
    Option and Either
    22

    View Slide

  23. @pacoworks
    Throwback Slide
    Meetup.talks.every().attendees.every().id {
    meetup.modify(f)
    }
    23

    View Slide

  24. @pacoworks
    Functions all the way down
    IdSetter compose ListEach compose
    AttendeesSetter compose ListEach compose
    TalkSetter compose MeetupSetter
    24

    View Slide

  25. @pacoworks
    Working in your domain
    @optics sealed class NetworkResult
    @optics data class Success(val content: Meetup): NetworkResult()
    @optics sealed class NetworkError : NetworkResult()
    @optics data class HttpError(val message: String): NetworkError()
    object TimeoutError: NetworkError()
    25

    View Slide

  26. @pacoworks
    More of the same
    NetworkResult.success.meetup.talks.every().attendees.every().id {
    result.getOrModify()
    }
    NetworkResult.networkError.httpError.message {
    result.getOrModify()
    }
    26

    View Slide

  27. @pacoworks
    arrow-optics
    Do I need to access a field?
    Do I need to modify a field?
    Can I match just this one sealed class?
    Can I get the element at index I?
    Can I update just the value at key K?
    27
    Interm
    ediate

    View Slide

  28. @pacoworks
    Seeing the forest
    val projects =
    MultiProjects(“All”, NonEmptyList(
    Project(“optics”, Budget(100)),
    MultiProjects(“Basics”, NonEmptyList(
    Project(“core”, Budget(1)),
    Project(“data”, Budget(5)),
    )),
    Project(“generic”, Budget(75)),
    ))
    28

    View Slide

  29. @pacoworks
    Seeing trees
    sealed class Project
    data class MultiProjects(
    val category: String,
    val l: NonEmptyList>
    ): Project()
    data class SingleProject(val name: String, val info: A)
    data class Budget(moneys: Int)
    val projects: Project = ...
    29

    View Slide

  30. @pacoworks
    arrow-recursion
    Abstraction for recursive data
    structures, including lists and trees
    Adds helper functions to traverse, fold,
    and map these structures
    Special thanks to Aedan Smith
    30
    Advanced

    View Slide

  31. @pacoworks
    Constructing and
    deconstructing data
    Serialization is a hard problem
    Deserialization is even worse!
    Extension functions for simple data?
    31

    View Slide

  32. @pacoworks
    Structural types
    32
    data class Account(val id: Int, val name: String, val balance: Int)
    typealias AccountTuple = Tuple3
    typealias AccountHList = HCons>>

    View Slide

  33. @pacoworks
    arrow-generic
    33
    @product data class Account(...)
    account.tupled()
    account.toHList()
    account + account
    (account, account, account).combineAll()
    Early days
    Interm
    ediate

    View Slide

  34. @pacoworks
    Are my abstractions
    anaemic?
    “I wish I had that RxJava operator for this”
    Duplicated code per class per design pattern or operator
    Shapes for generics don’t quite fit
    34
    getUserById(id)
    .flatMap { user ->
    Observable.merge(
    user.friends.map { friend ->
    getUserById(friend.id)
    }
    ).toList()
    }

    View Slide

  35. @pacoworks
    Extension interfaces
    Many of your classes will get lots of
    helpers if they implement certain functions:
    map: gets you another 6 extension functions
    foldLeft: gives 17! new extension functions
    flatMap: adds 8 extension functions and
    access to comprehensions
    35

    View Slide

  36. @pacoworks
    New functionality for
    everyone
    36
    Option> -> Option
    // Monad#flatten
    Try, Try, Try -> Try>
    // Applicative#tupled
    SetK, (User -> Boolean) -> Option
    // Foldable#find
    ListK> -> DeferredK>
    MapK> -> Either>
    // Traverse#sequence

    View Slide

  37. @pacoworks
    Comprehensions for everyone
    37
    bindingCatch {
    val user = getUserById(id).bind()
    val friendProfiles =
    user.friends.map { getUserById(it.id).bind() }
    friendProfiles
    }

    View Slide

  38. @pacoworks
    Coding to the interface
    38
    interface Dependencies:
    MonadError,
    Traverse,
    Eq,
    ProvidesNetwork
    fun Dependencies.requestTeams(
    ids: List
    ): Kind> = ...

    View Slide

  39. @pacoworks
    Coding to the interface
    39
    tryDeps.requestTeams(listOf(1,2,3)).fix()
    // Try>
    eitherDeps.requestTeams(listOf(1,2,3)).fix()
    // Either>
    optionDeps.requestTeams(listOf(1,2,3)).fix()
    // Option>

    View Slide

  40. @pacoworks
    arrow-typeclasses
    Interfaces defining extension functions
    that can be implemented using 1-3 new
    functions
    You can replace concrete classes with
    these interfaces for generic behavior
    Extensive test suite rooted on CS theory
    40
    Interm
    ediate

    View Slide

  41. @pacoworks
    41

    View Slide

  42. @pacoworks
    Dependency Management
    42
    arrow-instances-(core, data) provides
    implementations of typeclasses for
    datatypes coming from Arrow
    arrow-dagger-* provides Modules to
    inject those instances using Dagger 2
    Interm
    ediate

    View Slide

  43. @pacoworks
    arrow-effects
    val obs: ObservableKW> = getUserFriends(“123”).fix()
    val io: IO> = getUserFriends(“123”).fix()
    val def: Deferred> = getUserFriends(“123”).fix()
    43
    Generic behavior for threading and side-effects across
    frameworks
    Same expectations for operators
    Shared test suite
    Starter

    View Slide

  44. @pacoworks
    arrow-free
    44
    Interpreter for operation DSLs
    Synchronous, asynchronous, parallel, or
    sequential execution
    Debug, print as a string, optimize…
    Advanced

    View Slide

  45. @pacoworks
    Growing an
    ecosystem
    45

    View Slide

  46. @pacoworks
    Ank
    Available
    arrow/ank
    46
    Verify your docs by compiling
    snippets
    Output the results of
    snippets in the docs
    themselves
    Available for Java and Kotlin
    Built with arrow-free

    View Slide

  47. @pacoworks
    Helios
    Alpha
    47deg/helios
    47
    A screaming fast JSON
    library
    DSL syntax
    Extension via typeclasses
    Built with arrow-optics
    and arrow-generic

    View Slide

  48. @pacoworks
    Kollect
    Alpha
    47deg/kollect
    48
    A data access library for
    databases and network
    Allows paralellisation,
    caching, batching
    Built with arrow-effects
    and arrow-free

    View Slide

  49. @pacoworks
    Kor
    Stable
    Sefford/kor
    49
    A clean architecture
    implementation
    Includes Use Cases and
    Repositories
    Built with arrow-core and
    arrow-data

    View Slide

  50. @pacoworks
    Kessel
    Pre-alpha
    xenomachina/kessel
    50
    Parser combinator library
    Provides tokenizator and
    parsing
    Future migration to GLL
    parsing
    Built with arrow-core and
    arrow-data

    View Slide

  51. @pacoworks
    arrow-streams
    Pre-alpha
    51
    An abstraction over
    reactive streams across
    libraries
    Configuration for your own
    effect and executor
    abstraction
    Built with arrow-effects

    View Slide

  52. @pacoworks
    arrow-android?
    Preproduction
    52
    Input accepted :D

    View Slide

  53. @pacoworks
    Sharing
    understanding
    53

    View Slide

  54. @pacoworks
    Arrow Docs
    arrow-kt.io/docs/
    54
    Large community project
    Tens of contributors
    Contributing is a great
    way to learn!

    View Slide

  55. @pacoworks
    FP in Kotlin with Arrow
    youtu.be/1h4X8CrMjVs
    55
    Video series on youtube
    Linked atop of each doc
    Bi-weekly releases

    View Slide

  56. @pacoworks
    Talks, books and courses
    arrow-kt.io/docs/
    quickstart/blogs/
    56
    Functional Kotlin @ pakt
    Functional programming in
    Kotlin with Arrow @ caster.io
    Kotlin Funcional @
    Functionalhub
    Hands On Functional Kotlin @
    udemy

    View Slide

  57. @pacoworks
    Community <3
    gitter.im/arrow-kt/
    57
    73 github contributors
    Gitter.im chat
    Kotlinlang slack #arrow

    View Slide

  58. @pacoworks
    Arrow: arrow-kt.io
    pacoworks.com
    @pacoworks
    github.com/pakoito
    Slides: tinyurl.com/ArrowKotliners18
    58
    Special thanks to all
    Arrow contributors &
    supporters!

    View Slide