Last active
September 5, 2021 12:58
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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