Skip to content

Instantly share code, notes, and snippets.

@fancellu
Last active September 5, 2021 12:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fancellu/19830bdd1a6b3c52164326b9dde4e8b8 to your computer and use it in GitHub Desktop.
Save fancellu/19830bdd1a6b3c52164326b9dde4e8b8 to your computer and use it in GitHub Desktop.
Example of how to use the Cats Reader Monad to compose functionality and process some fixed type. DI in a functional style
Dave
52
User(Dave,53)
53
Running out for Dave, random=1943140383
Dave 52 Hello Dave, you are 52 years old
Running out for Dave, random=274775680
Dave 52 Hello Dave, you are 52 years old
Running out for John, random=-732708083
John 12 Hello John, you are 12 years old
Dave is living in Epsom. Hello Dave, you are 52 years old
import cats._
import cats.data.{Reader, _}
import cats.implicits._
import scala.util.Random
// Example of how to use the Cats Reader Monad to compose functionality and process some fixed type
// We compose pure functions with for comprehensions or map/Flatmap combinators
// Useful for when you find yourself passing some context again and again
object ReaderExample extends App {
case class User(name: String, age: Int)
val name: Reader[User, String] = Reader(_.name)
val age: Reader[User, Int] = Reader(_.age)
val makeOlder: Reader[User, User] = Reader(usr => usr.copy(age = usr.age + 1))
val user = User("Dave", 52)
// note we don't need to call run, apply calls run, so we can call it like a function
println(name(user))
println(age(user))
val olderUser = makeOlder(user)
println(olderUser)
println(age(olderUser))
// here we compose a reader from existing readers
// they all take the same implied type, User
val sayHello: Reader[User, String] = for {
name <- name
age <- age
} yield s"Hello $name, you are $age years old"
// And here we compose again
// Takes a User and processes it in order
val out: Reader[User, String] = for {
name <- name
age <- age
greeting <- sayHello
random = Random.nextInt()
_ = println(s"Running out for $name, random=$random")
} yield s"$name $age $greeting"
println(out(user))
println(out(user))
println(out(User("John", 12)))
// Here we process an Address, which contains a User
case class Address(town: String, tenant: User)
val town: Reader[Address, String] = Reader(_.town)
// see how we compose new readers based on existing readers in another type
val tenant: Reader[Address, String] = Reader(addr => name(addr.tenant))
val sayHelloTenant: Reader[Address, String] = Reader(addr => sayHello(addr.tenant))
val addrAndGreeting: Reader[Address, String] = for {
town <- town
tenant <- tenant
greeting <- sayHelloTenant
} yield s"$tenant is living in $town. $greeting"
println(addrAndGreeting(Address("Epsom", user)))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment