Skip to content

Latest commit

 

History

History
39 lines (31 loc) · 1.42 KB

Eff.md

File metadata and controls

39 lines (31 loc) · 1.42 KB

Eff

Imagine you are working with an application that contains the following effects:

  • Future
  • Reader
  • Either

The following function demonstrates this. Given a DBConfig, the function makes an async database call that may fail with a SQLException or succeed with a User:

def getUser(userId: Int): Reader[DBConfig, Future[Either[SQLException, User]] = ...

This function is called by the following function:

def getUserFirstName(userId: Int): Reader[DBConfig, Future[Either[SQLException, UserFirstName]] = {
  val user = getUser(userId)
  user.map(_.map(_.map(_.firstName)))
}

The problem here is that you have nested monads, which have to be mapped over several times. Introducing Eff eliminates this problem.

type ReadDBConfig[R] = MemberIn[Reader[DBConfig, ?], R]
type Err[R] = MemberIn[Either[SQLException, ?], R]
// _Async is an out of the box effect
def getUser[R: ReadDBConfig : _Async : Err](userId: Int): Eff[R, User] = ...

The code above describes a getUser function that has Reader, Future and Either effects. The return type of the function implies that the end result of the effectful stack is a User. Now getUserFirstName is much simpler to implement. mapping a function A => B over an Eff[R, A] gives you a Eff[R, B].

def getUserFirstName[R: ReadDBConfig : _Async : Err](userId: Int): Eff[R, UserFirstName] = {
  val user = getUser(userId)
  user map (_.firstName)
}