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.

B5ab42cbb595d2267cb2eaa050ec58c5?s=128

pakoito

June 15, 2018
Tweet

Transcript

  1. 3.

    @pacoworks Where does Arrow fit in your day to day

    code? Looking past the basic constructs What projects are using Arrow, today 3
  2. 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
  3. 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
  4. 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
  5. 8.

    @pacoworks This can become familiar fun deepUpdate(m: Meetup, f: (Id)

    -> Id) = Meetup.talks.every().attendees.every().id { meetup.modify(f) } 8
  6. 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!
  7. 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.
  8. 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
  9. 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<Database, Network> Up to 21 elements Starter
  10. 15.

    @pacoworks arrow-core My function return can fail, throw, or branch

    out Either, Option, Try 15 fun find(l: List<A>, f: (A) -> Boolean): Option<Tuple2<Int, A>> Starter
  11. 16.

    @pacoworks arrow-core I need this function to be lazy or

    cache its results Eval 16 fun foldRight(lb: Eval<B>, f: (A, Eval<B>) -> Eval<B>): Eval<B> Starter
  12. 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
  13. 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 }
  14. 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 }
  15. 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
  16. 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
  17. 24.

    @pacoworks Functions all the way down IdSetter compose ListEach<Attendee> compose

    AttendeesSetter compose ListEach<Talks> compose TalkSetter compose MeetupSetter 24
  18. 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
  19. 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
  20. 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
  21. 29.

    @pacoworks Seeing trees sealed class Project<A> data class MultiProjects<A>( val

    category: String, val l: NonEmptyList<Project<A>> ): Project<A>() data class SingleProject(val name: String, val info: A) data class Budget(moneys: Int) val projects: Project<Budget> = ... 29
  22. 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
  23. 31.

    @pacoworks Constructing and deconstructing data Serialization is a hard problem

    Deserialization is even worse! Extension functions for simple data? 31
  24. 32.

    @pacoworks Structural types 32 data class Account(val id: Int, val

    name: String, val balance: Int) typealias AccountTuple = Tuple3<Int, String, Int> typealias AccountHList = HCons<Int, HCons<String, HCons<Int, HNil>>>
  25. 33.

    @pacoworks arrow-generic 33 @product data class Account(...) account.tupled() account.toHList() account

    + account (account, account, account).combineAll() Early days Interm ediate
  26. 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() }
  27. 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
  28. 36.

    @pacoworks New functionality for everyone 36 Option<Option<Companies>> -> Option<Companies> //

    Monad#flatten Try<Id>, Try<Name>, Try<Count> -> Try<Tuple3<Id, Name, Count>> // Applicative#tupled SetK<User>, (User -> Boolean) -> Option<User> // Foldable#find ListK<DeferredK<UserDto>> -> DeferredK<List<UserDto>> MapK<Id, Either<Error, User>> -> Either<Error, Map<Id, User>> // Traverse#sequence
  29. 37.

    @pacoworks Comprehensions for everyone 37 bindingCatch { val user =

    getUserById(id).bind() val friendProfiles = user.friends.map { getUserById(it.id).bind() } friendProfiles }
  30. 38.

    @pacoworks Coding to the interface 38 interface Dependencies<F>: MonadError<F>, Traverse<F>,

    Eq<Team>, ProvidesNetwork fun <F> Dependencies<F>.requestTeams( ids: List<TeamId> ): Kind<F, List<Team>> = ...
  31. 39.

    @pacoworks Coding to the interface 39 tryDeps.requestTeams(listOf(1,2,3)).fix() // Try<List<Team>> eitherDeps.requestTeams(listOf(1,2,3)).fix()

    // Either<Throwable, List<Team>> optionDeps.requestTeams(listOf(1,2,3)).fix() // Option<List<Team>>
  32. 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
  33. 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
  34. 43.

    @pacoworks arrow-effects val obs: ObservableKW<List<User>> = getUserFriends(“123”).fix() val io: IO<List<User>>

    = getUserFriends(“123”).fix() val def: Deferred<List<User>> = getUserFriends(“123”).fix() 43 Generic behavior for threading and side-effects across frameworks Same expectations for operators Shared test suite Starter
  35. 44.

    @pacoworks arrow-free 44 Interpreter for operation DSLs Synchronous, asynchronous, parallel,

    or sequential execution Debug, print as a string, optimize… Advanced
  36. 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
  37. 47.

    @pacoworks Helios Alpha 47deg/helios 47 A screaming fast JSON library

    DSL syntax Extension via typeclasses Built with arrow-optics and arrow-generic
  38. 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
  39. 49.

    @pacoworks Kor Stable Sefford/kor 49 A clean architecture implementation Includes

    Use Cases and Repositories Built with arrow-core and arrow-data
  40. 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
  41. 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
  42. 54.

    @pacoworks Arrow Docs arrow-kt.io/docs/ 54 Large community project Tens of

    contributors Contributing is a great way to learn!
  43. 55.

    @pacoworks FP in Kotlin with Arrow youtu.be/1h4X8CrMjVs 55 Video series

    on youtube Linked atop of each doc Bi-weekly releases
  44. 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