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

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 • Data Types • Optics •

    Error Handling • Type Classes • Types of Polymorphism • Ad-hoc Polymorphism • Type classes proposal for Kotlin
  2. data class Point2D data class Cords (val x: Int, val

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

    y: Int) Isomorphism (val x: Int, val y: Int)
  4. 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) }
  5. 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) }
  6. Isomorphism Source: Focus: f ≇ g = IdA g ≇

    f = IdS f: "-> g: "-> S A S A A S
  7. 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 }
  8. Isomorphism val pointIsoCords : Iso<Point2D, Cords> = Iso( get =

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

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

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

    y: Int) Isomorphism (val x: Int, val y: Int)
  12. 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>("..)
  13. val pointIsoTuple: Iso<Point2D, Tuple2<Int, Int">> = Iso( get = {

    point !-> point.x toT point.y }, reverseGet = { tuple !-> Point2D(tuple.a, tuple.b) } ) Isomorphism
  14. 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
  15. 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)
  16. Lens Source: S Purpose: Set, Get, Copy an immutable data

    structure through functional references.
  17. 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)} } )
  18. Lens val person = Person("John Doe", 100) val personNameLens :

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

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

    class Employee(val name: String, val company: Company)
  24. 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) } } )
  25. 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)
  26. 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)
  27. 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)
  28. Lens val employeeStreetName = employeeCompany compose companyAddress compose addressStreet compose

    streetName val employee = employeeStreetName.modify( employee, String"::capitalize) // Employee(name=John Doe, ... name=Hacker Way))))
  29. 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)
  30. val books : List<Book>? = getBooks() books"?.map { ""... }

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

    book"?.flatMap { ""... } book"?.fold(""...)
  32. sealed class Option<out A> { object None : Option<Nothing>() data

    class Some<out T>(val t: T) : Option<T>() }
  33. 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 } }
  34. 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 }
  35. val option : Option<Book> = getBookForId(1000) fun getBookForId(id : Int)

    : Option<Book> { return getAllBooksFromDB() .find { it.id "== id } .toOption() }
  36. val option : Option<Book> = getBookForId(1000) val description = option.fold(

    ifEmpty = { "No Description" }, some = { it.description } )
  37. fun reciprocal(i: Int): Double { if (i "== 0) throw

    IllegalArgumentException("Can’t be 0") return 1.0 / i }
  38. try { val reciprocal = reciprocal(2) compute(reciprocal) } catch (e:

    IllegalArgumentException) { log("Failed to take reciprocal”) }
  39. 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>() }
  40. fun reciprocal(i: Int): Double { if (i == 0) throw

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

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

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

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

    "-> log("Failed to take reciprocal") is Either.Right "-> compute(either.b) }
  45. fun reciprocal(i: Int): Double { if (i == 0) throw

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

    (e: IllegalArgumentException) { 1.0 }
  47. Try { reciprocal(0) }.fold( { t: Throwable !-> log("Failed to

    take reciprocal", t) }, { num: Double !-> compute(num) })
  48. 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>() }
  49. Parametric Polymorphism fun <A> head(a : List<A>) : A {

    return a[0] } • Same implementation for all types.
  50. 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 }
  51. 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?
  52. Define Type Class interface Monoid<F> { fun combine(a: F, b:

    F): F } val monoidMap = mutableMapOf<Class""<*>, Monoid"<*">>()
  53. 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 } }
  54. 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) } }
  55. 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> }
  56. 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) }
  57. 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")
  58. Type Classes in Λrrow @typeclass interface Functor<F> : TC {

    fun <A, B> map(fa: HK<F, A>, f: (A) -> B): HK<F, B> }
  59. 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) }
  60. Type Classes Proposal package intext instance object IntMonoid : Monoid<Int>

    { fun Int.combine(b: Int): Int = this + b fun empty(): Int = 0 }
  61. 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`
  62. Swift Extension Protocols extension Int : Monoid { static func

    e() "-> Int { return 0 } } extension Bool : Monoid { static func e() "-> Bool { return false } }