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

Functional Programming with Arrow

Mohit S
January 24, 2018

Functional Programming with Arrow

Discussion of data types and type classes available in the library called Arrow for Kotlin.

Mohit S

January 24, 2018
Tweet

More Decks by Mohit S

Other Decks in Programming

Transcript

  1. Functional Programming
    with Λrrow
    Mohit Sarveiya

    View Slide

  2. View Slide

  3. View Slide

  4. Functional Programming with Λrrow
    • Data Types

    • Optics

    • Error Handling

    • Type Classes

    • Types of Polymorphism

    • Ad-hoc Polymorphism

    • Type classes proposal for Kotlin

    View Slide

  5. Optics
    Iso
    Lens

    View Slide

  6. Optics
    Iso
    Lens

    View Slide

  7. data class Point2D
    data class Cords
    (val x: Int, val y: Int)
    Isomorphism
    (val x: Int, val y: Int)

    View Slide

  8. data class Point2D
    data class Cords
    (val x: Int, val y: Int)
    Isomorphism
    (val x: Int, val y: Int)

    View Slide

  9. data class Point2D
    data class Cords
    (val x: Int, val y: Int)
    Isomorphism
    (val x: Int, val y: Int)
    fun point2DToCords (point2D: Point2D): Cords {
    return Cords(point2D.x, point2D.y)
    }

    View Slide

  10. data class Point2D
    data class Cords
    (val x: Int, val y: Int)
    Isomorphism
    (val x: Int, val y: Int)
    fun cordsToPoint2D (cord: Cord): Point2D {
    return Point2D(cord.x, cord.y)
    }

    View Slide

  11. Isomorphism
    point2DToCords: Point2D "-> Cords
    cordsToPoint2D: Cords "-> Point2D

    View Slide

  12. Isomorphism
    Source:
    Focus:
    f ≇ g = IdA
    g ≇ f = IdS
    f: "->
    g: "->
    S
    A
    S A
    A S

    View Slide

  13. Isomorphism
    interface Iso {
    /**
    * Get the focus of a [Iso]
    "*/
    fun get(s: S): A
    /**
    * Get the modified focus of a [Iso]
    "*/
    fun reverseGet(b: B): T
    }

    View Slide

  14. Isomorphism
    val pointIsoCords : Iso =
    Iso(
    get = { point !-> Cords(point.x, point.y) },
    reverseGet = { cords !-> Point2D(cords.x, cords.y) }
    )

    View Slide

  15. Isomorphism
    val point = Point2D(6, 10)
    val cords: Cords = pointIsoCords.get(point)
    "// Cords(6, 10)

    View Slide

  16. Isomorphism
    val cords = Cords(6, 10)
    val point2D: Point2D = pointIsoCords.reverseGet(cords)
    "// Point2D(6, 10)

    View Slide

  17. data class Point2D
    data class Tuple2
    (val x: Int, val y: Int)
    Isomorphism
    (val x: Int, val y: Int)

    View Slide

  18. data class Tuple2(val a: A, val b: B)
    Isomorphism
    data class Tuple3(val a: A,
    val b: B,
    val c: C)
    data class Tuple4("..)

    View Slide

  19. val pointIsoTuple: Iso> =
    Iso(
    get = { point !-> point.x toT point.y },
    reverseGet = { tuple !-> Point2D(tuple.a, tuple.b) }
    )
    Isomorphism

    View Slide

  20. var point2D = Point2D(1, 2)
    val tuple2 = pointIsoTuple.get(point2D)
    "// Tuple2(a=1, b=2)
    val point2D = pointIsoTuple.reverseGet(tuple2)
    "// Point2D(x=1, y=2)
    Isomorphism

    View Slide

  21. val pointIsoTuple = Iso(
    get = { point !-> Tuple2(point.x, point.y) },
    reverseGet = { tuple !-> Point2D(tuple.a, tuple.b) }
    )
    Generating Isomorphism
    data class Point2D (val x: Int, val y: Int)

    View Slide

  22. @Retention(SOURCE)
    @Target(CLASS)
    annotation class isos
    Generating Isomorphism

    View Slide

  23. Generating Isomorphism
    @isos data class Point2D (val x: Int, val y: Int)

    View Slide

  24. Optics
    Iso
    Lens

    View Slide

  25. Lens
    Source: S
    Purpose: Set, Get, Copy an immutable data structure
    through functional references.

    View Slide

  26. Lens
    Lens
    Get : S "-> A
    Set : A "-> (S "-> S)

    View Slide

  27. Lens
    data class Person(val name: String, val age: Int)

    View Slide

  28. Lens
    data class Person(val name: String, val age: Int)
    val personNameLens : Lens = Lens(
    get = { person !-> person.name },
    set = { newName !-> {foo !-> foo.copy(name = newName)} }
    )

    View Slide

  29. Lens
    val person = Person("John Doe", 100)
    val personNameLens : Lens = Lens(
    get = { person !-> person.name },
    set = { newName !-> {foo !-> foo.copy(name = newName)} }
    )

    View Slide

  30. Lens
    val person = Person("John Doe", 100)
    val newPerson = personNameLens.set(person, "John Doe Jr”)
    // Person(name=John Doe Jr, age=100)

    View Slide

  31. Lens
    data class Employee(val name: String, val company: Company)
    data class Company(val name: String, val address: Address)
    data class Address(val city: String, val street: Street)
    data class Street(val number: Int, val name: String)

    View Slide

  32. Lens
    val street = Street(1, "hacker Way”)
    val address = Address("Menlo Park, CA", street)
    val company = Company("Facebook", address)
    val employee = Employee("John Doe", company)

    View Slide

  33. Lens
    employee.copy(
    company = employee.company.copy(
    address = employee.company.address.copy(
    street = employee.company.address.street.copy(
    name = employee.company.address.street.name.capitalize()
    )
    )
    )
    )

    View Slide

  34. Lens
    data class Street(val number: Int, val name: String)
    data class Employee(val name: String, val company: Company)

    View Slide

  35. val company: Company)
    Lens
    data class Employee(val name: String, val company: Company)
    val employeeCompany: Lens =
    Lens(get = { it.company },
    set = { company !->
    { employee !->
    employee.copy(company = company)
    }
    }
    )

    View Slide

  36. data class Company(val name: String, val address: Address)
    Lens
    val companyAddress: Lens =
    Lens(
    get = { it.address },
    set = { address !->
    { company !->
    company.copy(address = address)
    }
    })
    val address: Address)

    View Slide

  37. data class Address(val city: String, val street: Street)
    Lens
    val addressStreet: Lens =
    Lens(
    get = { it.street },
    set = { street !-> {
    address !->
    address.copy(street = street)
    }
    })
    val street: Street)

    View Slide

  38. data class Street(val number: Int, val name: String)
    Lens
    val streetName: Lens = Lens(
    get = { it.name },
    set = { name !-> { street !-> street.copy(name = name) } }
    )
    val name: String)

    View Slide

  39. Lens
    val employeeStreetName = employeeCompany compose
    companyAddress compose
    addressStreet compose
    streetName
    val employee = employeeStreetName.modify(
    employee,
    String"::capitalize)
    // Employee(name=John Doe, ... name=Hacker Way))))

    View Slide

  40. val company: Company)
    Generating Lenses
    val employeeCompany: Lens =
    Lens(get = { it.company },
    set = { company !->
    { employee !->
    employee.copy(company = company)
    }
    }
    )
    data class Employee(val name: String, val company: Company)

    View Slide

  41. Generating Lenses
    @Retention(SOURCE)
    @Target(CLASS)
    annotation class lenses

    View Slide

  42. Generating Lens
    @lenses
    data class Employee(val name: String,
    val company: Company)

    View Slide

  43. Error Handling
    Option
    Either
    Try

    View Slide

  44. Error Handling
    Option
    Either
    Try

    View Slide

  45. val books : List? = getBooks()

    View Slide

  46. val books : List? = getBooks()
    books"?.map { ""... }
    books"?.flatMap { ""... }
    books"?.fold(""...)

    View Slide

  47. val book : Book? = getBookForId(1000)

    View Slide

  48. val book : Book? = getBookForId(1000)
    book"?.map { ""... }
    book"?.flatMap { ""... }
    book"?.fold(""...)

    View Slide

  49. Option
    A
    Some None

    View Slide

  50. sealed class Option {
    object None : Option()
    data class Some(val t: T) : Option()
    }

    View Slide

  51. val option : Option = getBookForId(1000)
    fun getBookForId(id : Int) : Option {
    val book : Book? = getAllBooksFromDB().find {
    it.id "== id
    }
    return if (book "!= null) {
    Some(book)
    } else {
    None
    }
    }

    View Slide

  52. val option : Option = getBookForId(1000)
    fun getBookForId(id : Int) : Option {
    val book : Book? = getAllBooksFromDB().find {
    it.id "== id
    }
    }
    return if (book "!= null) {
    Some(book)
    } else {
    None
    }

    View Slide

  53. val option : Option = getBookForId(1000)
    fun T?.toOption(): Option =
    if (this != null) {
    Some(this)
    } else {
    None
    }

    View Slide

  54. val option : Option = getBookForId(1000)
    fun getBookForId(id : Int) : Option {
    return getAllBooksFromDB()
    .find { it.id "== id }
    .toOption()
    }

    View Slide

  55. val option : Option = getBookForId(1000)
    val totalPrice: Option = option.map { book !->
    book.price + tax
    }

    View Slide

  56. val option : Option = getBookForId(1000)
    val description = option.fold(
    ifEmpty = {
    "No Description"
    },
    some = {
    it.description
    }
    )

    View Slide

  57. Error Handling
    Option
    Either
    Try

    View Slide

  58. fun reciprocal(i: Int): Double {
    if (i "== 0)
    throw IllegalArgumentException("Can’t be 0")
    return 1.0 / i
    }

    View Slide

  59. throw IllegalArgumentException(“Can’t be 0")
    fun reciprocal(i: Int): Double {
    if (i == 0)
    return 1.0 / i
    }

    View Slide

  60. try {
    val reciprocal = reciprocal(2)
    compute(reciprocal)
    } catch (e: IllegalArgumentException) {
    log("Failed to take reciprocal”)
    }

    View Slide

  61. Either
    A
    Left Right
    B

    View Slide

  62. sealed class Either {
    data class Left(val a: A) : Either()
    data class Right(val b: B) : Either()
    }

    View Slide

  63. fun reciprocal(i: Int): Double {
    if (i == 0)
    throw IllegalArgumentException(“Can’t be 0")
    return 1.0 / i
    }

    View Slide

  64. fun reciprocal(i: Int): {
    if (i == 0)
    throw IllegalArgumentException(“Can’t be 0")
    return 1.0 / I
    }
    Either

    View Slide

  65. fun reciprocal(i: Int): {
    return if (i == 0) {
    } else {
    1.0 / i
    }
    }
    Either.left(IllegalArgumentException(“Can’t be 0")
    Either

    View Slide

  66. fun reciprocal(i: Int): {
    return if (i == 0) {
    } else {
    }
    }
    Either.left(IllegalArgumentException(“Can’t be 0")
    Either
    Either.right(1.0 / i)

    View Slide

  67. fun reciprocal(i: Int): {
    return if (i == 0) {
    } else {
    }
    }
    Either.left(IllegalArgumentException(“Can’t be 0")
    Either
    Either.right(1.0 / i)

    View Slide

  68. val either: Either = reciprocal(2)

    View Slide

  69. val either: Either = reciprocal(2)
    when(either) {
    is Either.Left "-> log("Failed to take reciprocal")
    is Either.Right "-> compute(either.b)
    }

    View Slide

  70. Error Handling
    Option
    Either
    Try

    View Slide

  71. fun reciprocal(i: Int): Double {
    if (i == 0)
    throw IllegalArgumentException(“Can’t be 0")
    return 1.0 / i
    }
    External Library

    View Slide

  72. var reciprocal : Double = try {
    reciprocal(0)
    } catch (e: IllegalArgumentException) {
    1.0
    }

    View Slide

  73. val reciprocal = Try {
    reciprocal(2)
    }.getOrDefault {
    1.0
    }

    View Slide

  74. Try {
    reciprocal(0)
    }.fold(
    { t: Throwable !->
    log("Failed to take reciprocal", t)
    },
    { num: Double !->
    compute(num)
    })

    View Slide

  75. sealed class Try {
    data class Failure(val e: Throwable) : Try()
    data class Success(val value: A) : Try()
    }

    View Slide

  76. Adhoc Polymorphism & Type Classes

    View Slide

  77. Type Classes
    Functor, Monad, Monoid, Applicative …
    Type Constructors
    Option, Try, Either, IO, Ior, Reader, State

    View Slide

  78. Polymorphism
    Ad-hoc Universal
    Overloading Coercion Inclusion Parametric

    View Slide

  79. Parametric Polymorphism
    fun head(a : List) : A {
    return a[0]
    }
    • Same implementation for all types.

    View Slide

  80. Ad-hoc Polymorphism
    fun combineAll(a: Matrix, b: Matrix) : Matrix {
    return doMatrixComputation()
    }
    • Same name with different implementation.

    • Type Dependent.
    fun combineAll(a: String, b: String) : String {
    return a + b
    }

    View Slide

  81. Ad-hoc Polymorphism with Type Class
    inline fun combineAll(
    a: F, b: F,
    ev: Monoid = monoidal()
    ): F {
    return ev.combine(a, b)
    }
    • Polymorphism with Type Classes.

    • How do I achieve this?

    View Slide

  82. Define Type Class
    interface Monoid {
    fun combine(a: F, b: F): F
    }

    View Slide

  83. Define Type Class
    interface Monoid {
    fun combine(a: F, b: F): F
    }
    val monoidMap = mutableMapOf, Monoid"<*">>()

    View Slide

  84. Define Instances of Type Class
    val monoidMap = mutableMapOf, Monoid"<*">>()
    monoidMap[String"::class.java] = object: Monoid {
    override fun combine(a: String, b: String): String {
    return a + b
    }
    }

    View Slide

  85. Define Instances of Type Class
    val monoidMap = mutableMapOf, Monoid"<*">>()
    monoidMap[Matrix"::class.java] = object: Monoid {
    override fun combine(a: Matrix, b: Matrix): Matrix {
    return addMatricies(a, b)
    }
    }

    View Slide

  86. Getting Instances of Type Class
    val monoidMap = mutableMapOf, Monoid"<*">>()
    inline fun monoidal(): Monoid {
    return monoidMap[F"::class.java] as Monoid
    }

    View Slide

  87. Ad-hoc Polymorphism with Type Class
    inline fun combineAll(
    a: F, b: F,
    ev: Monoid = monoidal()
    ): F {
    return ev.combine(a, b)
    }

    View Slide

  88. Ad-hoc Polymorphism with Type Class
    inline fun combineAll(
    a: F, b: F,
    ev: Monoid = monoidal()
    ): F {
    return ev.combine(a, b)
    }
    combineAll(matrix, matrix)
    combineAll("Hello", "World")

    View Slide

  89. Type Classes in Λrrow
    @typeclass
    interface Functor : TC {
    fun map(fa: HK, f: (A) -> B): HK
    }

    View Slide

  90. Type Classes Instance in Λrrow
    @instance(Option"::class)
    interface OptionFunctorInstance : Functor {
    override fun map(
    fa: OptionKind, f: kotlin.Function1
    ): Option =
    fa.ev().map(f)
    }

    View Slide

  91. View Slide

  92. Type Classes Proposal
    typeclass Monoid {
    fun A.combine(b: A): A
    fun empty(): A
    }

    View Slide

  93. Type Classes Proposal
    package intext
    instance object IntMonoid : Monoid {
    fun Int.combine(b: Int): Int = this + b
    fun empty(): Int = 0
    }

    View Slide

  94. Type Classes Proposal
    import intext.IntMonoid
    1.combine(2) "// 3
    Int.empty() "// 0

    View Slide

  95. Type Classes Proposal
    import intext.IntMonoid
    fun add(a: A, b: A): A given Monoid = a.combine(b)
    add(1, 1) "// compiles
    add("a", "b") "// does not compile: No `String: Monoid`

    View Slide

  96. Swift Extension Protocols
    extension Int : Monoid {
    static func e() "-> Int {
    return 0
    }
    }
    extension Bool : Monoid {
    static func e() "-> Bool {
    return false
    }
    }

    View Slide

  97. Suggestions to Learn more!

    View Slide

  98. View Slide

  99. View Slide

  100. View Slide

  101. View Slide

  102. Thank you!
    twitter.com/heyitsmohit

    github.com/msya

    View Slide