Elements of Functional Programming



Elements of Functional Programming

0 0


scala-intro-slides


On Github lifecoder / scala-intro-slides

Elements of Functional Programming

Popular Functional Languages

  • Scala
  • F#
  • Haskell
  • Clojure
  • Scheme
  • Ocaml

Scala Quick Tour

Variables

val x = 10
x = 20 // error!
var y = 30
y = 40 // OK
          

Case classes

case class Point(x: Int, y: Int)
val p1 = Point(10, 20)
val p2 = Point(0, p1.y)
          

Functions

object Utils {
  def times(x: Int, n: Int): Int =
    x * n
  def twice(x: Int) = times(2, x)
}
          

Lambda Expression

def twice(f: Int => Int, x: Int): Int =
  f(f(x))

twice((x: Int) => x * 3, 10)
twice(x => x * 3, 10)
twice(_ * 3, 10)
          

Parametric Polymorphism

def twice[T](f: T => T, x: T): T =
  f(f(x))
          

Currying

def twice[T](f: T => T)(x: T): T =
  f(f(x))

val plusFour: Int => Int = twice(_ + 2)
plusFour(10)
twice((_: Int) + 2)(10)
          

ADT

sealed trait MyBool
case object True extends MyBool
case object False extends MyBool
def not(b: MyBool): MyBool = b match {
  case True => False
  case False => True
}
          

ADT

sealed trait E
case class Plus(a: E, b: E) extends E
case class Const(x: Int) extends E

Pattern Matching

def eval(e: E): Int = e match {
  case Const(x) => x
  case Plus(a, b) => eval(a) + eval(b)
}
val e: E =
  Plus(
    Const(10),
    Plus(Const(2), Const(3)))
println(eval(e)) // prints 15

Pattern Matching

def opt(e: E): E = e match {
  case Plus(Const(0), x) => x
  case Plus(x, Const(0)) => x
  case Plus(x, y) =>
    Plus(opt(x), opt(y))
  case c@Const(_) => c
}

Pattern Matching

val e: E =
  Plus(Const(10),
    Plus(Const(2), Const(0)))
println(opt(e))
// Prints: Plus(Const(10),Const(2))

Implicits

trait Logger {
  def output(s: String): Unit
}
def log(s: String)(implicit log: Logger) =
  log.output(s)

Implicits

implicit val logger = new Logger {
  override def output(s: String): Unit =
    println(s)
}
def work(): Unit = {
  log("Working...")
  log("Done!")
}

Type classes

trait Show[T] {
  def show(x: T): String
}

Type classes

def show[T](x: T)(implicit s: Show[T]): String =
  s.show(x)
def mkShow[T](f: T => String): Show[T] =
  new Show[T] {
    override def show(x: T): String = f(x)
  }

Type classes

implicit val stringShow: Show[String] =
  mkShow(x => x)
implicit val intShow: Show[Int] =
  mkShow(_.toString)
implicit
  def ls[T](implicit s: Show[T]): Show[List[T]] =
    mkShow(_.map(s.show).mkString(", "))

Type classes

println(show(10))
println(show("Hello"))
println(show(List(1, 2, 3)))
println(show('a')) // error!

Equality type class

true == true
"hello" == UserId(32)
2 == 2.0
dbConnection == dbConnection

Equality type class

trait Eq[T] {
  def equal(a: T, b: T): Boolean
}

Equality operations

trait EqOps[T] {
  def ===(other: T): Boolean
}

Equality operations

implicit def toEqOps[T](t: T)(implicit e: Eq[T]): EqOps[T] =
  new EqOps[T] {
    def ===(other: T) =
      e.equal(t, other)
  }

Equality instances

implicit val intEq: Eq[Int] = new Eq[Int] {
  override def equal(a: Int, b: Int): Boolean =
    a == b
}

Equality instances

implicit def
tupleEq[A, B](implicit eqA: Eq[A], eqB: Eq[B]): Eq[(A, B)] =
  new Eq[(A, B)] {
    override def equal(a: (A, B), b: (A, B)): Boolean =
      eqA.equal(a._1, b._1) && eqB.equals(a._2, b._2)
  }

Equality instances

import EqualityExample._
println(1 === 1) // true
println(1 === 2) // false
println((1, 2) === (1, 2)) // true
println(4 === "test") // error

Optional values

sealed trait Opt[+T]
case class Some[T](x: T) extends Opt[T]
case object None extends Opt[Nothing]

Optional values

def div(a: Int, b: Int): Opt[Int] =
  if (b == 0) None else Some(a / b)

Optional values

def useDiv() = {
  val a = div(10, 5)
  println(a) // Some(2)
  val b = div(10, 0)
  println(b) // None
}

Optional values

sealed trait Opt[+T] {
  def isDefined: Boolean
  def get: T
}

Optional values

case class Some[T](x: T) extends Opt[T] {
  override def isDefined: Boolean = true
  override def get: T = x
}

Optional values

case object None extends Opt[Nothing] {
  override def isDefined: Boolean = false
  override def get: Nothing =
    throw new RuntimeException("Error")
}

Optional values

def useDiv() = {
  val a = div(10, 5)
  if (a.isDefined)
    println(a.get)
  val b = div(10, 0)
  if (b.isDefined)
    println(b.get)
}

Optional values

sealed trait Opt[+T] {
  def map[R](f: T => R): Opt[R] = this match {
    case None => None
    case Some(x) => Some(f(x))
  }
}

Optional values

def useDiv() = {
  val a = div(10, 5)
  println(a.map(_ + 1)) // prints Some(3)
  val b = div(10, 0).map(_ + 1)
  println(b) // prints None
}

Optional values

def useDiv() = {
  val a = div(10, 5).map(_ + 1)
  println(a)
  // error in next line: A is Opt[Int]
  val b = div(a, 2).map(_ + 2)
  println(b)
}

Optional values

sealed trait Opt[+T] {
  def flatMap[R](f: T => Opt[R]): Opt[R] =
    this match {
      case None => None
      case Some(x) => f(x)
    }
}

Optional values

def useDiv() = {
  val a = div(10, 5)
  val b = a.flatMap(x => div(x, 2).map(_ + 1))
  println(b) // None
}

Optional values

def useDiv2() = {
  val res = for {
    a <- div(10, 5)
    b <- div(a, 2)
  } yield b + 1
  println(res) // Some(2)
}

Flat map chain

val a = div(10, 5)
  .flatMap(x => div(x, 0))
  .flatMap(x => div(x, 2))
  .flatMap(x => Some(x + 1))
println(a) // None
          

Flat map chain

val a = div(10, 5)
  .flatMap(div(_, 0))
  .flatMap(div(_, 2))
  .flatMap(x => Some(x + 1))
println(a) // None
          

Optional

sealed trait Opt[+T] {
  def isDefined: Boolean // Bad
  def get: T // Bad
  def getOrElse[B >: T](y: => B): B =
    this match {
      case Some(x) => x
      case None => y
    }
}
          

Optional

div(10, 0).getOrElse(0) // 0
          

Optional

div(10, 0) match {
  case Some(x) => println(x)
  // error: missing case
}
          

Optional

div(10, 0) match {
  case Some(x) => println(x)
  case None => println("Nope.")
}
          

Either

sealed trait Either[A, B]
case class Left[A, B](a: A) extends Either[A, B]
case class Right[A, B](b: B) extends Either[A, B]
          

Either

def f(): Either[Int, String] =
  Left(10)
def g() = {
  f() match {
    case Left(a) => println("Left " + a)
    case Right(b) => println("Right " + b)
  }
}

          

Demo

Elements of Functional Programming