Findings, Interesting Topics About FP & Scala with :cat2::cat:

bg 50% right:25%


Content

Terminology

Type Classes

trait Showable[A] {
  def show(a: A): String
}

bg fit


Monads


trait Monad[F[_]] {
  def pure[A](a: A): F[A] // wrap it

  def flatMap[A, B](value: F[A])(func: A => F[B]): F[B]
  // apply a transformation function to a monadic value

  def map[A, B](value: F[A])(func: A => B): F[B] =
    flatMap(value)(a => pure(func(a)))
    // apply a non-monadic function to a monadic value
}

50% center


Cool Monads from scalaz

Functor

// Through Functor#compose Functor[List].compose[Option].map(listOption)(_ + 1) // res1: List[Option[Int]] = List(Some(2), None, Some(3))

---
- Laws:
  - Composition: `fa.map(f).map(g)` is `fa.map(f.andThen(g))`
  - Identity: Mapping with the identity function is a no-op
    `fa.map(x => x) = fa`
- Allow lifting pure to effectful function
  ```scala
   def lift[A, B](f: A => B): F[A] => F[B] = fa => map(fa)(f)

// Example implementation for Option
implicit val functorForOption: Functor[Option] = new Functor[Option] {
  def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa match {
    case None    => None
    case Some(a) => Some(f(a))
  }
}

Applicatives


case class Foo(s: Symbol, n: Int)

val maybeFoo = for {
  s <- maybeComputeS(whatever)
  n <- maybeComputeN(whatever)
} yield Foo(s, n)

⬇️

val maybeFoo = maybeComputeS(whatever).flatMap(
  s => maybeComputeN(whatever).map(n => Foo(s, n))
)

maybeComputeN never depends on s, compiler still thinks so

val maybeFoo = (maybeComputeS(whatever) |@| maybeComputeN(whatever))(Foo(_, _))

val url = new URL(urlStr)
(S3Bucket(url) |@| S3Key(url)) { (bucket, key) =>
  if (bucket == store.bucket) {
    key.some
  } else {
    warn(s"can not delete a resource $url from unknown bucket: $bucket")
    none
  }
}.flatten.toList

Monoids

trait Monoid[A] {
  def combine(x: A, y: A): A
  def empty: A
}

Examples of Monoids:


Usage

trait TotalInstances {

  implicit def totalMonoid(implicit targetCcy: Currency, mc: MoneyContext): Monoid[Total] =
    Monoid.instance[Total]({ (a, b) =>
      a + b
    }, zero)

  def zero(implicit targetCcy: Currency, mc: MoneyContext): Total = {
    val zeroCurrency = targetCcy.apply(0).toResponse
    Total(
      total = zeroCurrency,
      product = zeroCurrency,
      realProduct = zeroCurrency,
      duties = None,
      insurance = None,
      returns = None,
      shipping = zeroCurrency,
      taxes = None,
      discount = None
    )
  }
}

Kleisli

val f: Int => Task[String] = ???
val fKleisli: Kleisli[Task, Int, String] = ???

Like a monad Transformer for function composition. –> The advantage is you can do it within the context of a Future / Task, etc.

andThenK does automatic lifting.

Call run to extract again.


import cats.data._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import cats.instances.future._

object KleisliTest extends App {
  val getNumberFromDb: Unit => Future[Int]  = _ => Future.successful(2)
  val processNumber: Int => Future[Int]     = num => Future.successful(num * 2)
  val persistToDb: Int => Future[Boolean]   = _ => Future.successful(true)

  val kleisliCombo: Kleisli[Future, Unit, Boolean] = 
    Kleisli(getNumberFromDb)
    andThen processNumber
    andThen persistToDb

  val unpacked: Unit => Future[Boolean] = kleisliCombo.run

  unpacked().map(println)
}

Algebraic Data Types


Illegal states

Command("foo", None, None)
Command("bar", Some(1), Some(2))

Reworked

sealed abstract class Command extends Product with Serializable

object Command {
  final case class Move(meters: Int) extends Command
  final case class Rotate(degrees: Int) extends Command
}

Bonus: Pattern Matching

def print(cmd: Command) = cmd match {
  case Command.Move(dist)    => println(s"Moving by ${dist}m")
  case Command.Rotate(angle) => println(s"Rotating by ${angle}°")
}

Thanks!