attr_reveal | author | reveal | reveal_plugins | reveal_theme | reveal_trans | title | ||
---|---|---|---|---|---|---|---|---|
:frag t |
|
split |
(highlight notes) |
sky |
slide |
Functional Programming in Scala |
revealtitleslide:nil
Or, let's face it... copying
Runar Bjarnasson - Introduction to Functional Programming
- Bill Carlson
- Innovation Developer at Cotiviti Labs
- I get paid to write purely functional programs!
Programming with Functions
A function f: A => B
maps any value from one set to exactly one
value in another set.
And nothing else
- Totality: A function will return a single value for all values
of
A
. - Determinism: Every time you call a function with the same arguments, you will always get the same result.
- Purity: The result of the function is the only effect.
You can replace all occurrences of a function call with the result of that call without changing the program.
val string = "some text"
val s1 = string.reverse
val s2 = string.reverse
val s3 = s1 + s2
val sb = new StringBuilder("some text")
val s1 = sb.reverse
val s2 = sb.reverse
val s3 = s1 append s2
- Console?
- File access?
- Network?
- Exceptions?
We'll get to that...
Oh, boy... live code...
val isEven: Int > Boolean = i => i % 2 =
0 def divisibleBy(k: Int):
Int > Boolean = i -> i % k =
0 val isEven = divisibleBy(2)
type Predicate[A] = A => Boolean val not[A](p: Predicate[A]): Predicate[A] = a => !p(a) val and[A](p1: Predicate[A], p2: Predicate[A]): Predicate[A] = a => p1(a) && p2(a) val or[A](p1: Predicate[A], p2: Predicate[A]): Predicate[A] = a => p1(a) || p2(a)
val lift[A](f: (Boolean, Boolean) => Boolean): (Predicate[A], Predicate[A]) => Predicate[A] = (p1, p2) => a => f(p1(a), p2(a))
val and[A] = lift(_ && ) val or[A} = lift( || _)
Empty List: Nil
Non-empty list (cons): A :: List[A]
To build large lists, just add to a smaller list:
val list1 = 2 :: 3 :: 4 :: 5 :: Nil // List(2, 3, 4, 5)
val list2 = 1 :: list1
def sum(ints: List[Int]): Int = ints match { case Nil => 0 case x :: xs => x + sum1(xs) }
def sum2(ints: List[Int]): Int = { @annotation.tailrec def loop(acc: Int, remaining: List[Int]): Int = remaining match { case Nil => acc case x :: xs => loop(acc + x, xs) } loop(0, ints) }
What about product? mkString?
Generate foldLeft (and foldRight)
Show how to implement the above using foldLeft
def sum(xs: List[Int]): Int = foldLeft(xs, 0)(_ + ) def product(xs: List[Int]): Int = foldLeft(xs, 1)( * ) def mkString(xs: List[Int]): String = foldLeft(xs, "")( + _.toString) def reverse[A](xs: List[A]): List[A] = foldLeft(xs, List.empty[A]) { (acc, i) => i :: acc }
def map[A, B](xs: List[A])(f: A => B): List[B] = foldRight(xs, List.empty[B]) { (i, acc) => f(i) :: acc } def mapl[A, B](xs: List[A])(f: A => B): List[B] = reverse(foldLeft(xs, List.empty[B]) { (acc, i) => f(i) :: acc })
Some[A]
None
def fold[A](o: Option[A], z: B)(f: A => B): B = o match {
case Some(a) => f(a)
case None => z
}
Left[E]
Right[A]
def fold[E, A](e: Either[E, A], z: E => B, f: A => B): B = e match {
case Left(e) => z(e)
case Right(a) => f(a)
}
- Your program is the data structure
- Your interpreter is the fold
- GoF Interpreter Pattern
Can this be extended?
def ????(i: Int): Int
def inc(i: Int) = i + 1
def timesTwo(i: Int) = i * 2
def abs(i: Int) = if (i < 0) -i else i
???
def ????[A](i: A): A
def identity[A](i: A): A = i
Only true in the case of purity. Otherwise, it could also do something like:
def bar[A](i: A) = i.asInstanceOf[Int] + 1
or
def bar[A}(i: A) = throw new RuntimeException("BWAHAHAHAHA!")
[T]he purpose of abstracting is not to be vague, but to create a new semantic level in which one can be absolutely precise.
-- Edsger W. Dijkstra, "The Humble Programmer"
- Console?
- File access?
- Network?
- Exceptions?
Remember this slide?
sealed trait Console[A]
case class Print(s: String) extends Console[Unit]
case object Read extends Console[Option[String]]
sealed trait File[A]
case class Open(p: Path) extends File[Unit]
case class Write(data: Array[Byte]) extends File[Unit]
case object Read extends File[Array[Byte]]
case object Truncate extends File[Unit]
An algebra is an abstract set of operations
Provides laws which must hold true
Using algebras, combinators, and folds, we simplify (evaluate) the program to a single value.
...maybe a good topic for next time?
Bill Carlson