Sunday, 26 April 2020

Scala with Cats: Answers to revision questions

I'm studying the 'Scala with Cats' book. I want the information to stick so I am applying a technique from 'Ultralearning' where I make revision notes in the questions which I regularly review. This post contains the questions and their answers.

Chapter 1: Introduction: Answers

  1. What is a type class? 
    • It’s a way of defining some behaviour that many types can adhere to. In scala it's a trait with a parameter
  2. What is an interface syntax in cats?
  3. What do Show and Eq do in cats? 
    • Members of show can be presented as strings. 
    • object Main extends App {
      import cats._
      import cats.implicits._
      println(123.show)
      println("xyc".show)
      println(true.show)
      // using show on custom objects
      case class Cat(name: String)
      val cat = Cat("Ruby")
      implicit val catShow: Show[Cat] = Show.show[Cat](c => "Cat called " + c.name)
      println(cat.show)
      }
  4. Explain covariance, contravariance and invariance. 
    • Covariance
    • object Main extends App {
      /*
      Covariant: F[B] is a subtype of F[A] if B is a subtype of A
      */
      trait Shape {}
      case class Circle(radius: Double) extends Shape
      /*
      if we change this to [A] or [-A] we cannot pass
      covariantCircle into func
      */
      trait Covariant[+A] {
      def print(): Unit
      }
      val covariantCircle: Covariant[Circle] = () => println(s"Circle")
      val covariantShape: Covariant[Shape] = () => println(s"Shape")
      def func(c: Covariant[Shape]): Unit = c.print()
      func(covariantCircle)
      func(covariantShape)
      }
      view raw Covariant.scala hosted with ❤ by GitHub
    • Contravariance
    • object Main extends App {
      /*
      Contravariant: F[B] is a subtype of F[A] if A is a subtype of B
      */
      trait Shape {}
      case class Circle(radius: Double) extends Shape
      /*
      if we change this to [A] or [+A] we cannot pass
      covariantShape into func
      */
      trait Contravariant[-A] {
      def print(): Unit
      }
      val covariantCircle: Contravariant[Circle] = () => println(s"Circle")
      val covariantShape: Contravariant[Shape] = () => println(s"Shape")
      def func(c: Contravariant[Circle]): Unit = c.print()
      func(covariantCircle)
      func(covariantShape)
      }
    • Invariance
    • F[A] and F[B] are never subtypes of each other, regardless of the relationship between A and B.

Chapter 2: Monoids and Semigroups: Answers

  1. Define monoid and semigroup and give examples of each. 
  2. // eg union of non empty sets
    trait MySemigroup[A] {
    // must be associative
    def combine(a1: A, a2: A): A
    }
    // eg union of sets, where the empty set is the identity element
    trait MyMonoid[A] extends MySemigroup[A] {
    // is an identity element
    def empty: A
    }

Chapter 3: Functors: Answers

  1. What is a type constructor? 
    • A type constructor is like a type but it has a 'hole to fill'. Eg List is a type constructor because it has one 'hole' to fill. Note the difference between List (a type constructor) and List[A] (a type, using a type parameter) and List[Int] (a concrete type).
  2. What  is a  covariant functor? 
  3. /*
    Functor Laws:
    1. Identity
    fa.map(a => a) == fa
    2. Composition
    fa.map(f).map(g) = fa.map(g(f(_)))
    */
    trait MyFunctor[F[_]] {
    def map[A, B](fa: F[A], f: A => B): F[B]
    }
  4. Write a method 'power' which can operate on either List(1,2,3) or Option(2) and returns List(1,4,9) and Option(4) respectively. 
  5. object Main extends App {
    import cats._
    import cats.implicits._
    def power[F[_]](f: F[Int])(implicit func: Functor[F]): F[Int] = {
    f.map(x => x*x)
    }
    println(power(List(1,2,3)))
    println(power(Option(2)))
    // alternative syntax
    def power2[F[_] : Functor](in: F[Int]): F[Int] = {
    in.map(i => i * i)
    }
    }
  6. What is a contravariant functor? 
  7. // has a contramap method
    trait MyContravariantFunctor[F[_]] {
    def contramap[A, B](fa: F[A], f: B => A): F[B]
    }
    // We can use contramap to build a show for Salary from the show for Money
    import cats._
    import cats.implicits._
    case class Money(amount: Int)
    case class Salary(size: Money)
    implicit val showMoney: Show[Money] = Show.show(m => s"$$${m.amount}")
    implicit val showSalary: Show[Salary] = showMoney.contramap(_.size)
    println(Money(5).show)
    println(Salary(Money(50)).show)

Chapter 4: Monads

  1. What is a monad? 
  2. /*
    3 Monad laws
    1. Right identity: pure(a).flatMap(func) == func(a)
    2. m.flatMap(pure) = m
    3. m.flatMap(f).flatMap(g) = m.flatMap(x => f(x).flatMap(g))
    */
    trait MyMonad[F[_]] {
    def pure[A](a: A): F[A]
    def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B]
    // all monads are functors because we can define map in terms of pure and flatMap
    def map[A, B](fa: F[A], f: A => B): F[B] = flatMap(fa)(a => pure(f(a)))
    }
  3. What is the 'Id' monad for? 
    • Imagine you have a method which operates on a Monad of Ints, eg power method so for Option(2) it returns Option(4) and for List(1,2) it returns List(1,4). To use this method on regular ints we need to wrap our plain Ints in a monad, hence Id. 
  4. What is the 'Eval' monad for? 
    • This is for abstracting over different modes of computation, eager or lazy. Memoized computations run once then are cached, and can run lazily or eagerly. vals are eager and memoized. Defs are lazy and non memoized. Lazy vals are lazy and memoized. Eval has three subtypes, Now, Later and Always. 
    • It also has 'defer' which can be used to avoid stack overflow in recursive methods.
  5. What does each line print? 
  6. object Main extends App {
    import cats._
    import cats.implicits._
    private val now: Eval[Double] = Eval.now(math.random)
    println(now.value)
    println(now.value)
    private val later: Eval[Double] = Eval.later(math.random)
    println(later.value)
    println(later.value)
    private val always: Eval[Double] = Eval.always(math.random)
    println(always.value)
    println(always.value)
    }
    • 'Now' is eager and memoized (val). 'Later' is lazy and memoized (lazy val). 'Always' is lazy and not memoized (def). 
  7. What is the output of the following code snippet and why? 
  8. object Main extends App {
    import cats._
    import cats.implicits._
    val ans: Eval[Int] = for {
    a <- Eval.now { println("a"); 40 }
    b <- Eval.always { println( "b" ); 2 }
    c <- Eval.later { println( "c" ); 2 }
    } yield {
    println("Adding!")
    a + b + c
    }
    println("---")
    ans.value // ignored
    ans.value // ignored
    }
    • "Now" is calculated eagerly so is printed first. However mapping functions are always called like defs so the output is 'a, ---, b, c, Adding!, b, c, Adding!' There is a memoize function that can be used when chaining. 
  9. What is the writer monad for?
    • This is for carrying a log along with some computation. It's particularly useful in multithreaded computation where normal log messages might get interleaved. Writer[W,A] has a log of type W and a result of type A.
  10. What is the reader monad for? 
    • Reader[A,B] represents a function A => B. It allows sequencing of operations that depend on some input. 
  11. What is the state monad for? 
    • The state monad allows us to pass additional state around as part of a computation. We can use it to model mutable state in a purely functional way, without the mutation. State[S, A] represents functions of type S => (S, A). S is the type of the state and A is the result type. 

Chapter 5: Monad Transformers

  1. What are monad transformers used for? 
    • These are for composing monads, eg for giving us nice ways of handling Future[Option[T]]


Scala with Cats: Revision Questions



Earlier this year I read a fantastic book about Ultralearning. One tip from the book was to make notes in the form of questions without the answers rather than making notes to browse. Therefore, to review my work I can answer the questions and test my recall rather than just reading information and hope it goes in. 

The answers are in another post

Chapter 1: Introduction

  1. What is a type class? 
  2. What is an interface syntax in cats?
  3. What do Show and Eq do in cats? 
  4. Explain covariance, contravariance and invariance. 

Chapter 2: Monoids and Semigroups

  1. Define monoid and semigroup and give examples of each. 

Chapter 3: Functors

  1. What is a type constructor? 
  2. What is a covariant functor?
  3. Write a method 'power' which can operate on either List(1,2,3) or Option(2) and returns List(1,4,9) and Option(4) respectively. 
  4. What is a contravariant functor? 

Chapter 4: Monads

  1. What is a monad? 
  2. What is the 'Id' monad for? 
  3. What is the 'Eval' monad for? 
  4. What does each line print? 
  5. object Main extends App {
    import cats._
    import cats.implicits._
    private val now: Eval[Double] = Eval.now(math.random)
    println(now.value)
    println(now.value)
    private val later: Eval[Double] = Eval.later(math.random)
    println(later.value)
    println(later.value)
    private val always: Eval[Double] = Eval.always(math.random)
    println(always.value)
    println(always.value)
    }
  6. What is the output of the following code snippet? 
  7. object Main extends App {
    import cats._
    import cats.implicits._
    val ans: Eval[Int] = for {
    a <- Eval.now { println("a"); 40 }
    b <- Eval.always { println( "b" ); 2 }
    c <- Eval.later { println( "c" ); 2 }
    } yield {
    println("Adding!")
    a + b + c
    }
    println("---")
    ans.value // ignored
    ans.value // ignored
    }
  8. What is the writer monad for?
  9. What is the reader monad for? 
  10. What is the state monad for? 




Thursday, 16 August 2018

Cake pattern

(don't read this post if you are eating because it might make you throw up)

trait DataReader {
def readName(): String
}
trait DummyDataReader extends DataReader {
def readName(): String = "bEllA"
}
class GreeterService {
self: DataReader =>
def greet(): Unit = {
val formattedName = self.readName().toLowerCase.capitalize
println("Hi there, " + formattedName)
}
}
object Main extends App {
val greeter = new GreeterService() with DummyDataReader
greeter.greet()
// if you want to know why people hate the cake pattern then here is the answer:
greeter.readName()
// wtf?! It exposes all the methods of all the dependencies! Grim
}
view raw Cake.scala hosted with ❤ by GitHub

Wednesday, 15 August 2018

Partial Unification


/*
This doesnt compile because foo is expecting a type
class which takes one type parameter but Function1
takes two
*/
object DoesntCompile {
def foo[F[_], A](fa: F[A]): String = fa.toString
foo { x: Int => x * 2 }
// which is the same as the following
val meow: Function1[Int, Int] = {x: Int => x * 2}
foo(meow)
}
/*
We can make it compile by creating a type alias, Cat,
which fixes the the first type parameter to be Int
*/
object DoesCompile {
def foo[F[_], A](fa: F[A]): String = fa.toString
type Cat[A] = Function1[Int, A]
val meow: Cat[Int] = {x: Int => x * 2}
foo(meow)
}
/*
This is a bit nasty isn't it!
The other way to make it compile, without defining this
special Cat type alias, is to enable Partial Unification
in our build.sbt, which handles it for us.
scalacOptions += "-Ypartial-unification"
*/



Tuesday, 2 January 2018

Writer Monad from Scala Cats

Once upon a time there was some stinky side affecting code with println all over the place.


object Cinderella1 extends App {
def f(): Int = {
println("Generating random int")
42
}
def g(i: Int): Int = {
println("Applying complex mathematical formula")
i * 2
}
g(f())
}
How horrible! No, Cinderella, you definitely can't meet the prince of MonadLand looking like that!
Fortunately her Functional Fairy Godmother is on hand.



 'Ah ha' she cries! From each function you need to return both the int (which is the existing return type) and some Strings to represent the logs!

'Easy peasy' she says and with a swish of her magic wand she transforms Cinderella.

object Cinderella2 extends App {
def f: (Int, Vector[String]) = {
(42, Vector("Generating random int"))
}
def g(i: Int): (Int, Vector[String]) = {
(i % 2, Vector("Applying complex mathematical formula"))
}
val (i, vs1) = f
val (result, vs2) = g(i)
val printStatements = vs1 ++ vs2
println(printStatements)
println(result)
}
Well I guess this is ok, but look how much more effort Cinderella has to go to to compose f and g! That's not very elegant at all!

'Please, Fairy Godmother', Cinderella begs, 'Isn't there anything more you can do?'

Now it's a little-known Disney secret that Cinderella's Functional Fairy Godmother is actually an enormous fan of Cats and has a clever Writer monad trick up her sleeve.

object Cinderella3 extends App {
import cats.data.Writer
import cats.instances.vector._
import cats.syntax.applicative._
import cats.syntax.writer._
type Logged[A] = Writer[Vector[String], A]
def f: Logged[Int] = for {
n <- 42.pure[Logged]
_ <- Vector("Generating random int").tell
} yield n
def g(i: Int): Logged[Int] = for {
n <- i.pure[Logged]
_ <- Vector("Applying complex mathematical formula").tell
} yield n * 2
private val result1 = for {
i <- f
result <- g(i)
} yield result
private val result2 = f.flatMap(g)
println(result1)
println(result2)
}
Output:

WriterT((Vector(Generating random int, Applying complex mathematical formula),84))
WriterT((Vector(Generating random int, Applying complex mathematical formula),84))

'Oh thank you Functional Fairy Godmother', cries Cinderella, delighted.

FFG winks and whispers, 'I just love a good for comprehension! There are actually a lot of different ways of creating a writer!', and she slips some example code into Cinderella's pocket for later bedtime reading, in case things with the prince don't go to plan:

object DifferentWaysOfCreatingACatsWriter extends App {
import cats.data.Writer
import cats.instances.vector._
import cats.syntax.applicative._
import cats.syntax.writer._
type Logged[A] = Writer[Vector[String], A]
def f1: Logged[Int] = {
val magicNumber = 42
magicNumber.writer(Vector("Generating random int"))
}
def f2: Logged[Int] = for {
n <- 42.pure[Logged]
_ <- Vector("Generating random int").tell
} yield n
def f3: Logged[Int] = {
val magicNumber = (42
Writer(Vector("Generating random int"), magicNumber)
}
println(f1)
println(f2)
println(f3)
}
Cinderella was so intrigued by these examples that she stayed up all night reading Dave Gurnell and Noel Walsh's book Scala with Cats and skipped the ball entirely.


And she lived happily ever after. 

Friday, 29 December 2017

Varience in scala

Seriously I never remember which is which.

Covariance


sealed trait InMyPocket[+A]
sealed trait Cuddler
final case class Puppy(name: String) extends Cuddler
object VarianceExample {
var dog: InMyPocket[Puppy] = new InMyPocket[Puppy] {}
var cuddler: InMyPocket[Cuddler] = new InMyPocket[Cuddler] {}
cuddler = dog
// dog = cuddler // doesnt compile
}

Contravarience


sealed trait InMyPocket[-A]
sealed trait Cuddler
final case class Puppy(name: String) extends Cuddler
object VarianceExample {
var dog: InMyPocket[Puppy] = new InMyPocket[Puppy] {}
var cuddler: InMyPocket[Cuddler] = new InMyPocket[Cuddler] {}
// cuddler = dog // doesnt compile
dog = cuddler
}

Invariance


sealed trait InMyPocket[A]
sealed trait Cuddler
final case class Puppy(name: String) extends Cuddler
object VarianceExample {
var dog: InMyPocket[Puppy] = new InMyPocket[Puppy] {}
var cuddler: InMyPocket[Cuddler] = new InMyPocket[Cuddler] {}
// cuddler = dog // doesnt compile
// dog = cuddler // doesnt compile
}

Friday, 22 December 2017

Akka Persistence: From Untyped to typed

Vanilla Akka Persistence


Akka persistence involves three main concepts:

- Commands
- Events
- State

Consider the following two command objects:

case class WriteCommand(data: String)
case object PrintCommand

We are going to build an actor which responds to two sorts of commands 'Write!' and 'Print!'. When it receives a WriteCommand, the data in the command will be persisted and when it receives a PrintCommand it will print all the saved data to the console.

On receipt of a WriteCommand our actor will generate and persist a WriteEvent.

case class DataWriteEvent(data: String)

The actor will maintain an internal representation of the data it has persisted  ('state') . This is the data that will be printed to console on receipt of a PrintCommand.

The state has an update method which is called with a DataWriteEvent. The updated state is returned.

case class ExampleState(events: List[String] = Nil) {
  def updated(evt: DataWriteEvent): ExampleState = copy(evt.data :: events)
  def size: Int = events.length
  override def toString: String = events.reverse.toString
}

Wiring up the actor:

class ExamplePersistentActor extends PersistentActor {
  override def persistenceId = "example-id"
  var state = ExampleState()

  def updateState(event: DataWriteEvent): Unit = state = state.updated(event)

  def numberOfEvents: Int = state.size

  val receiveCommand: Receive = {
    case WriteCommand(data) ⇒
      persist(DataWriteEvent(s"$data-$numberOfEvents")) { event ⇒
        updateState(event)
        context.system.eventStream.publish(event)
      }
    case PrintCommand ⇒ println(state)
  }

  val receiveRecover: Receive = {
    case evt: DataWriteEvent ⇒ updateState(evt)
    case SnapshotOffer(_, snapshot: ExampleState) ⇒ state = snapshot
  }

}

And finally to run:

object Example extends App {
  val system = ActorSystem()
  val actor = system.actorOf(Props(new ExamplePersistentActor))
  actor ! WriteCommand("Ruby")
  actor ! PrintCommand
}

If I run it once I get the following:

List(Ruby-0)

And running it one more time:

List(Ruby-0, Ruby-1)

Moving on...


Receive is a function with a signature from Any => Unit which is pretty damn generic! Akka typed, the motivation for which is beyond the scope of this post, basically gives compile-time feedback on the correctness of your actor interactions. 

Sexy Types


This time we define a 'behaviour' in terms of Command, Event and State. This means that we need our 'Write!' and 'Print!' commands to share a type hierarchy: 

sealed trait TypedExampleCommand extends Serializable
case class TypedExampleWriteCommand(data: String) extends TypedExampleCommand
case object TypedExamplePrint extends TypedExampleCommand


Our event and state look pretty much the same as they did before:

case class TypedEvent(data: String)

case class TypedExampleState(events: List[String] = Nil) {
  def updated(evt: TypedEvent): TypedExampleState = copy(evt.data :: events)
  def size: Int = events.length
  override def toString: String = events.reverse.toString
}


 Our behaviour is defined with four parameters:

1. An id for the actor
2. An initial state
3. A function for converting commands to events (wrapped in 'Effects') and handling side effects
4. A function for updating the state given an event

object TypedExample {

  def behavior: Behavior[TypedExampleCommand] =
    PersistentActor
      .immutable[TypedExampleCommand, TypedEvent, TypedExampleState](
      persistenceId = "example-id", 
      initialState = new TypedExampleState,      
      commandHandler = PersistentActor.CommandHandler {
        (_, state, cmd) ⇒
          cmd match {
            case TypedExampleWriteCommand(data) =>
              Effect.persist(TypedEvent(s"$data ${state.size}"))
            case TypedExamplePrint =>
              println(state); Effect.none          
        }
      },     
      eventHandler = (state, event) ⇒ event match {
        case TypedEvent(_) => state.updated(event)
      }
    )
}

Now to run it:

object TypedExampleMain extends App {

  import akka.actor.typed.scaladsl.adapter._

  val system = akka.actor.ActorSystem("system")
  val actor = system.spawn(TypedExample.behavior, "example")
  actor ! TypedExampleWriteCommand("Hi Bella")
  actor ! TypedExamplePrint
}

Again running once gives:

List(Hi Bella 0)

And twice:

List(Hi Bella 0, Hi Bella 1)

Typed vs Untyped

Let's define a case object: 

case object FluffyKittenFace 

If we send a FluffyKittenFace message to our persistent actor then the message gets swallowed. The original code continues to work but nothing appears to happen. 

However if we try sending a FluffyKittenFace to our typed actor: 


Red squiggle of doom! 

We helpfully get a compile time error saying that our actor doesn't know what to do with all that fluff! 


FluffyKittenFace is going to have to find another actor to process her message, or become a TypedExampleCommand


Zee Cod:



https://github.com/polyglotpiglet/akka-typed-examples

Note that these examples are working against master rather than the release build. It should work from 2.5.9. 

Scala with Cats: Answers to revision questions

I'm studying the 'Scala with Cats' book. I want the information to stick so I am applying a technique from 'Ultralearning...