Functional Programming with Arrow

B3f560d34c14a9113e5024bc34ac26a0?s=47 Mohit S
January 24, 2018

Functional Programming with Arrow

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

B3f560d34c14a9113e5024bc34ac26a0?s=128

Mohit S

January 24, 2018
Tweet

Transcript

  1. Functional Programming with Λrrow Mohit Sarveiya

  2. None
  3. None
  4. Functional Programming with Λrrow • Data Types • Optics •

    Error Handling • Type Classes • Types of Polymorphism • Ad-hoc Polymorphism • Type classes proposal for Kotlin
  5. Optics Iso Lens

  6. Optics Iso Lens

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

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

    y: Int) Isomorphism (val x: Int, val y: Int)
  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) }
  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) }
  11. Isomorphism point2DToCords: Point2D "-> Cords cordsToPoint2D: Cords "-> Point2D

  12. Isomorphism Source: Focus: f ≇ g = IdA g ≇

    f = IdS f: "-> g: "-> S A S A A S
  13. Isomorphism interface Iso<S, T, A, B> { /** * Get

    the focus of a [Iso] "*/ fun get(s: S): A /** * Get the modified focus of a [Iso] "*/ fun reverseGet(b: B): T }
  14. Isomorphism val pointIsoCords : Iso<Point2D, Cords> = Iso( get =

    { point !-> Cords(point.x, point.y) }, reverseGet = { cords !-> Point2D(cords.x, cords.y) } )
  15. Isomorphism val point = Point2D(6, 10) val cords: Cords =

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

    pointIsoCords.reverseGet(cords) "// Point2D(6, 10)
  17. data class Point2D data class Tuple2 (val x: Int, val

    y: Int) Isomorphism (val x: Int, val y: Int)
  18. data class Tuple2<out A, out B>(val a: A, val b:

    B) Isomorphism data class Tuple3<out A, out B, out C>(val a: A, val b: B, val c: C) data class Tuple4<out A, out B, out C, out D>("..)
  19. val pointIsoTuple: Iso<Point2D, Tuple2<Int, Int">> = Iso( get = {

    point !-> point.x toT point.y }, reverseGet = { tuple !-> Point2D(tuple.a, tuple.b) } ) Isomorphism
  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
  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)
  22. @Retention(SOURCE) @Target(CLASS) annotation class isos Generating Isomorphism

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

    y: Int)
  24. Optics Iso Lens

  25. Lens Source: S Purpose: Set, Get, Copy an immutable data

    structure through functional references.
  26. Lens Lens<S, A> Get : S "-> A Set :

    A "-> (S "-> S)
  27. Lens data class Person(val name: String, val age: Int)

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

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

    Lens<Person, String> = Lens( get = { person !-> person.name }, set = { newName !-> {foo !-> foo.copy(name = newName)} } )
  30. Lens val person = Person("John Doe", 100) val newPerson =

    personNameLens.set(person, "John Doe Jr”) // Person(name=John Doe Jr, age=100)
  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)
  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)
  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() ) ) ) )
  34. Lens data class Street(val number: Int, val name: String) data

    class Employee(val name: String, val company: Company)
  35. val company: Company) Lens data class Employee(val name: String, val

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

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

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

    streetName: Lens<Street, String> = Lens( get = { it.name }, set = { name !-> { street !-> street.copy(name = name) } } ) val name: String)
  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))))
  40. val company: Company) Generating Lenses val employeeCompany: Lens<Employee, Company> =

    Lens(get = { it.company }, set = { company !-> { employee !-> employee.copy(company = company) } } ) data class Employee(val name: String, val company: Company)
  41. Generating Lenses @Retention(SOURCE) @Target(CLASS) annotation class lenses

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

    Company)
  43. Error Handling Option Either Try

  44. Error Handling Option Either Try

  45. val books : List<Book>? = getBooks()

  46. val books : List<Book>? = getBooks() books"?.map { ""... }

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

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

    book"?.flatMap { ""... } book"?.fold(""...)
  49. Option A Some None

  50. sealed class Option<out A> { object None : Option<Nothing>() data

    class Some<out T>(val t: T) : Option<T>() }
  51. val option : Option<Book> = getBookForId(1000) fun getBookForId(id : Int)

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

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

    = if (this != null) { Some(this) } else { None }
  54. val option : Option<Book> = getBookForId(1000) fun getBookForId(id : Int)

    : Option<Book> { return getAllBooksFromDB() .find { it.id "== id } .toOption() }
  55. val option : Option<Book> = getBookForId(1000) val totalPrice: Option<Int> =

    option.map { book !-> book.price + tax }
  56. val option : Option<Book> = getBookForId(1000) val description = option.fold(

    ifEmpty = { "No Description" }, some = { it.description } )
  57. Error Handling Option Either Try

  58. fun reciprocal(i: Int): Double { if (i "== 0) throw

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

    (i == 0) return 1.0 / i }
  60. try { val reciprocal = reciprocal(2) compute(reciprocal) } catch (e:

    IllegalArgumentException) { log("Failed to take reciprocal”) }
  61. Either A Left Right B

  62. sealed class Either<out A, out B> { data class Left<out

    A, out B>(val a: A) : Either<A, B>() data class Right<out A, out B>(val b: B) : Either<A, B>() }
  63. fun reciprocal(i: Int): Double { if (i == 0) throw

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

    be 0") return 1.0 / I } Either<Throwable, Double>
  65. fun reciprocal(i: Int): { return if (i == 0) {

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

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

    } else { } } Either.left(IllegalArgumentException(“Can’t be 0") Either<Throwable, Double> Either.right(1.0 / i)
  68. val either: Either<Throwable, Double> = reciprocal(2)

  69. val either: Either<Throwable, Double> = reciprocal(2) when(either) { is Either.Left

    "-> log("Failed to take reciprocal") is Either.Right "-> compute(either.b) }
  70. Error Handling Option Either Try

  71. fun reciprocal(i: Int): Double { if (i == 0) throw

    IllegalArgumentException(“Can’t be 0") return 1.0 / i } External Library
  72. var reciprocal : Double = try { reciprocal(0) } catch

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

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

    take reciprocal", t) }, { num: Double !-> compute(num) })
  75. sealed class Try<out A> { data class Failure<out A>(val e:

    Throwable) : Try<A>() data class Success<out A>(val value: A) : Try<A>() }
  76. Adhoc Polymorphism & Type Classes

  77. Type Classes Functor, Monad, Monoid, Applicative … Type Constructors Option,

    Try, Either, IO, Ior, Reader, State
  78. Polymorphism Ad-hoc Universal Overloading Coercion Inclusion Parametric

  79. Parametric Polymorphism fun <A> head(a : List<A>) : A {

    return a[0] } • Same implementation for all types.
  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 }
  81. Ad-hoc Polymorphism with Type Class inline fun <reified F> combineAll(

    a: F, b: F, ev: Monoid<F> = monoidal() ): F { return ev.combine(a, b) } • Polymorphism with Type Classes. • How do I achieve this?
  82. Define Type Class interface Monoid<F> { fun combine(a: F, b:

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

    F): F } val monoidMap = mutableMapOf<Class""<*>, Monoid"<*">>()
  84. Define Instances of Type Class val monoidMap = mutableMapOf<Class""<*>, Monoid"<*">>()

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

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

    inline fun <reified F> monoidal(): Monoid<F> { return monoidMap[F"::class.java] as Monoid<F> }
  87. Ad-hoc Polymorphism with Type Class inline fun <reified F> combineAll(

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

    a: F, b: F, ev: Monoid<F> = monoidal() ): F { return ev.combine(a, b) } combineAll(matrix, matrix) combineAll("Hello", "World")
  89. Type Classes in Λrrow @typeclass interface Functor<F> : TC {

    fun <A, B> map(fa: HK<F, A>, f: (A) -> B): HK<F, B> }
  90. Type Classes Instance in Λrrow @instance(Option"::class) interface OptionFunctorInstance : Functor<OptionHK>

    { override fun <A, B> map( fa: OptionKind<A>, f: kotlin.Function1<A, B> ): Option<B> = fa.ev().map(f) }
  91. None
  92. Type Classes Proposal typeclass Monoid<A> { fun A.combine(b: A): A

    fun empty(): A }
  93. Type Classes Proposal package intext instance object IntMonoid : Monoid<Int>

    { fun Int.combine(b: Int): Int = this + b fun empty(): Int = 0 }
  94. Type Classes Proposal import intext.IntMonoid 1.combine(2) "// 3 Int.empty() "//

    0
  95. Type Classes Proposal import intext.IntMonoid fun <A> add(a: A, b:

    A): A given Monoid<A> = a.combine(b) add(1, 1) "// compiles add("a", "b") "// does not compile: No `String: Monoid`
  96. Swift Extension Protocols extension Int : Monoid { static func

    e() "-> Int { return 0 } } extension Bool : Monoid { static func e() "-> Bool { return false } }
  97. Suggestions to Learn more!

  98. None
  99. None
  100. None
  101. None
  102. Thank you! twitter.com/heyitsmohit
 github.com/msya