Intro to Functional Programming

James Ward - Developer Advocate @ Google Cloud

What does this code do?

Is addOne() a function?

“If you can use math to do something, you should”

Philip Wadler

A "Pure" Function

  • Takes inputs and return outputs
  • Consistent mapping from inputs to outputs

						val a = addOne(2)
						val b = addOne(2)
					

						val b = a
					

Jargon Alert!

  • Referential Transparency
  • First-order Function

							def upper(s: String): String = {
								s.toUpperCase
							}
						

							val upper: String => String = (s: String) => s.toUpperCase
						

							val upper: String => String = _.toUpperCase
						

Why?

  • Deterministic
  • Provable
  • Composable

First-order Composition / Chaining


						val count = (s: String) => s.length
						count(upper("hello"))
					

How do I do anything useful?


						val handleRequest: Request => Response = { request =>
							...
						}
					

What about external state?


						val getUsers: () => List[User]
					

Functions(Functions)


						val cu = upper.andThen(count)
						cu("hello")

						val doSomethingToFirst = (s: String, f: Char => Char) => {
						  if (s.nonEmpty)
						    f(s.head) + s.tail
						  else
						    s
						}

						val d = doSomethingToFirst("asdf", _)
						d(_.toUpper)
					

Jargon Alert!

  • Higher-order Functions
  • Currying

Operating on Items in Containers

  • List, Option, Future, etc
  • transform (map), filter, reduce

						"hello".map(_.toUpper)

						Try("hello").map(_.toUpperCase)

						val f: String => String = _.toUpperCase
						val g: String => Int = _.length
						val h = f andThen g

						Try("hello").map(h)

						Some("asdf").filter(_.contains("a"))
						
						"hello".foldLeft(0)(_ + _.toInt)
					

Jargon Alert!

  • Functor
  • Foldable

								Try("hello").map(_ => Try("asdf")).flatten

								val num1 = Try(StdIn.readLine("Number 1: ").toInt)
								val num2 = Try(StdIn.readLine("Number 1 was ???; Number 2: ").toInt)
						

							import scala.io.StdIn
							import scala.util.Try
							
							for {
								num1 <- Try(StdIn.readLine("Number 1: ").toInt)
								num2 <- Try(StdIn.readLine("Number 2: ").toInt)
							} yield num1 + num2
					

Jargon Alert!

  • Monad

Now for something practical!


							for {
								num      <- client.url(randomNumUrl).get().map(_.body.toInt)
								wordReqs =  Seq.fill(num)(client.url(randomWordUrl).get().map(_.body))
								words    <- Future.sequence(wordReqs)
							} yield words.mkString(" ")
					

						("asdf", 2)

						case class Foo(name: String, age: Int)

						Foo("asdf", 1)


						sealed trait Bar
						case class FooBar() extends Bar
						case class BarBar() extends Bar

						val b: Bar = BarBar()
					

Jargon Alert!

  • Algebraic Data Types
  • Product Types
  • Sum Types

Pattern Matching


							foo match {
								case Foo(name, 2) => name
								case _ => "arggg!"
							}

							b match {
								case FooBar() => "foo"
								case BarBar() => "bar"
							}
					

Where to next?