Skip to content

Instantly share code, notes, and snippets.

@arosien
Last active March 14, 2018 23:09
Show Gist options
  • Save arosien/0aee59cc734042f7044d24bdaec731a8 to your computer and use it in GitHub Desktop.
Save arosien/0aee59cc734042f7044d24bdaec731a8 to your computer and use it in GitHub Desktop.
Notes and code from "Essential Scala: Six Core Concepts for Learning Scala", Seattle at the Sea 2018-03-18 (https://www.meetup.com/Seattle-Scala-User-Group/events/244100420/) You can get our (free!) "Essential Scala" book at https://underscore.io/books/essential-scala/
/*
* Algebraic Data Types
*/
// A has a B and C
case class A(b: B, c: C)
// A is a B or C
sealed trait A
case class B() extends A
case class C() extends A
// A website visitor is: logged in; or anonymous
// A logged in user has:
// an ID; and
// an email address
sealed trait Visitor {
def fold[B](
ifLoggedIn: (Long, String) => B,
ifAnonymous: () => B): B =
this match {
case LoggedIn(id, email) =>
ifLoggedIn(id, email)
case Anonymous() =>
ifAnonymous()
}
// how de we write toJson using fold?
def toJson2(): String =
fold(
(id, email) => s"{type:logged_in,id:$id, email:$email}",
() => "{type:anonymous}")
def toJson(): String =
this match {
case LoggedIn(id, email) => s"{type:logged_in,id:$id, email:$email}"
case Anonymous() => "{type:anonymous}"
}
}
case class LoggedIn(id: Long, email: String) extends Visitor
case class Anonymous() extends Visitor
// A calculation is a success or failure
// A success has a value.
// A failure has an error
sealed trait Calculation {
def add(value: Int): Calculation =
this match {
case Success(v) => Success(v + value)
case f @ Failure(msg) => f
}
def failLoudly(): Calculation =
this match {
case s: Success => s
case Failure(msg) => Failure(msg + "!!!")
}
}
case class Success(value: Int) extends Calculation
case class Failure(msg: String) extends Calculation
val calc = Success(12) // constructor
sealed trait MyList[A] {
def fold[B](
ifEmpty: () => B,
ifPair: (A, B) => B): B =
this match {
case Empty() =>
ifEmpty()
case Pair(head, tail) =>
ifPair(
head, // A
tail.fold(ifEmpty, f)) // B
}
}
case class Empty[A]() extends MyList[A]
case class Pair[A](head: A, tail: MyList[A]) extends MyList[A]
???
/*
* Structural Recursion
*/
// Calculation.add(value: Int): Calculation
???
/*
* Sequencing Computation
*/
// fold: A => B
???
// Convert user to JSON
???
// A Result is a Success with value A or a Failure (with no value)
sealed trait Result[A] {
def map[B](f: A => B): Result[B] =
this match {
case Success(a) => Success(f(a))
case f: Failure => f
}
}
case class Success[A](value: A) extends Result[A]
case class Failure[A]() extends Result[A]
???
// map: F[A] => (A => B) => F[B]
???
// Get user from database (might not be a user): Result[User]
def getUser(id: Long): Result[User]
// Get order for user (might not be an order): User => Result[Order]
def getOrder(user: User): Result[Order]
val order: Result[Order] =
getUser(12) // Result[User]
.flatMap(getOrder) // User => Result[Order]
???
// flatMap: F[A] => (A => F[B]) => F[B]
???
// Get user by id: UserId => Result[User]
// Get user's order: User => Result[Order]
// Transform order to JSON: (Order => Json) => Result[Json]
// Send JSON: Result[Json] => Response

— Essential Scala: Six Core Concepts for Learning Scala —

Adam Rosien @arosien Underscore https://underscore.io

Can you also read this, you back there?

There are very cool seats up front.

Introduction

  • Are you not a beginner?
    • Please let the beginners speak the most!
  • Scala is complex?
    • Self types
    • Type bounds
    • Existential types
    • Trait stacks
    • Overloading
    • Implicit conversions
  • It doesn’t have to be this way
  • 6 concepts
    1. Expressions, types, & values
    2. Objects and classes
    3. Algebraic data types
    4. Structural recursion
    5. Sequencing computation
    6. Type classes

Algebraic Data Types

  • Goal: translate data descriptions into code
  • Model data with logical ors and logical ands
  • Two patterns: product types (and) sum types (or)
  • Product type: A has a B and C
  • Sum type: A is a B or C
  • Sum and product together make algebraic data types

-> scala code

  • Summary
    • Structure data with logical ands and ors
    • These are called algebraic data types
    • Code follows immediately from structure of the data

Structural Recursion

  • Goal: transform algebraic data types
  • Structure of the code follows structure of the data
  • Two (sub-)patterns: pattern matching and polymorphism

-> Implement Calculation.add(value: Int): Calculation = ???

  • Summary
    • Processing algebraic data types immediately follows from the structure of the data
    • Can choose between pattern matching and polymorphism
    • Pattern matching (within the base trait) is usually preferred

Sequencing Computation

  • Goal: patterns for sequencing computations
  • Functional programming is about transforming values
  • That is all you can do without introducing side- effects
  • A => B => C
  • This is sequencing computations
  • Three patterns: fold, map, and flatMap
  • fold
    • A => B
    • Abstraction over structural recursion

    -> Convert user to JSON

  • map
    • F[A] => (A => B) => F[B]

    -> Get user from database (might not be a user): Result[User] -> Get order for user (might not be an order): User => Result[Order]

  • flatMap
    • F[A] => (A => F[B]) => F[B]

    -> Get user by id: UserId => Result[User] -> Get user’s order: User => Result[Order] -> Transform order to JSON: (Order => Json) => Result[Json] -> Send JSON: Result[Json] => Response

  • Summary
    • Standard patterns for sequencing computations
    • fold is general transformation for algebraic data types
    • map: F[A] => (A => B) => F[B]
    • flatMap: F[A] => (A => F[B]) => F[B]
    • You can teach monads in an introductory course!

Type Classes

  • Ad-hoc polymorphism
  • Break free from your class oppressors!
  • Concerns that cross class hierarchy
    • E.g. serialize to JSON
  • Common behaviour without (useful) common type
  • Abstract behaviour to a type class
  • Can implement type class instances in ad-hoc manner

Conclusions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment