We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies.

We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies. Less

We use cookies and other tracking technologies... More

Login or register
to apply for this job!

Login or register to start contributing with an article!

Login or register
to see more jobs from this company!

Login or register
to boost this post!

Show some love to the author of this blog by giving their post some rocket fuel 🚀.

Login or register to search for your ideal job!

Login or register to start working on this issue!

Engineers who find a new job through Functional Works average a 15% increase in salary 🚀

Blog hero image

Using Cat Data Reader Monad

Ayache Khettar 15 December, 2017 (2 min read)

Introduction

In this short post, I would like to introduce you the Reader monad — see cats documentation

The first thing you need to know is that the Reader monad represents a function: A => B, i.e:

class Reader[A,B](run: A => B) {
    // details omitted
}

We could use the Reader monad to achieve various things, but I would like to show you in this post how to use it to achieve composition and dependency injection.

Composition

Using the above class definition of the Reader we can create tow instances of a Reader with matching type.

import cats.data.Reader 
val upper = Reader((text: String) => text.toUpperCase) 

val greet = Reader((name: String) => s"Hello $name”)

We have two Readers with matching type, we can combine them as we like to produce a new Reader without the risk of producing a side effect.

val comb1 = upper.compose(greet) 

val comb2 = upper.andThen(greet) val result = comb1.run("Bob”)
println(result) // prints Hello Bob

Dependency Injection

Consider the two services below. The user can only register in a course if he is authorised. So the CourseService is dependent on the result of the AuthService:

case class Course(desc: String, code: String)

class AuthService {
  def isAuthorised(userName: String): Boolean = userName.startsWith("J")
}

class CourseService {
  def register(course: Course, 
               isAuthorised: 
               Boolean, 
               name: String) = {
    if (isAuthorised) 
      s"User $name registered for the course: ${course.code}"
    else 
      s"User: $name is not authorised to register for course: ${course.code}"
  }
}

Let’s create a class which represent the current environment and call it CourseManager.

case class CourseManager(course: Course,
                         userName: String,
                         authService: AuthService,
                         courseService: CourseService)

When the application is run the CourseManager will have all the services needed to carry out the business transaction.

Let’s create the Reader monad which allows us to write business methods not tied to the services

def isAuthorised = Reader[CourseManager, Boolean]{ courseMgr =>
  courseMgr.authService.isAuthorised(courseMgr.userName)
}

def register(isFull: Boolean) = Reader[CourseManager, String] { courseMgr =>
  courseMgr.courseService.register(courseMgr.course, 
                                   isFull, 
                                   courseMgr.userName)
}

Note that in both functions, the CourseManager shows up in the return type. Therefore we can combine these two functions using for comprehension as follow:

val result = for {
  authorised <- isAuthorised
  response <- register(authorised)
} yield response

The above result will not be executed until we run our application. Let’s run our application now:

val course = Course("Computer Science", "CS01")
val report = result.run(CourseManager(course, "Jon", new AuthService, new CourseService))
println(report) // prints: User Jon registered for the course: CS01

I hope this short introduction will lead you find out about other features provided by the TypeLevel Libraries.

cats2.png

Originally published on medium.com