Reader monad

The State monad represents an external (to the definition of the logic) state, which needs to be taken into account and possibly modified. The Reader monad is similar in the taking into account part—it accepts an external context and passes it over unchanged to every computation down the queue. In terms of the global state we discussed during the examination of the state monad, the Reader will have access to read-only system properties. Because of this, the reader monad is often known as a mechanism for dependency injection—because it takes some outside configuration (not necessarily basic things like strings or numbers, but also possibly other complex components, database access mechanisms, network sockets, or other resources) and makes it available for the function it wraps.

Let's see how Reader is defined. We have already compared State and Reader, and the definition is also quite similarwith the only difference that we don't need to return the changed context (it is read-only, after all). In code, it looks like this:

final case class Reader[R, A](run: R => A) {
def compose[B](f: A => Reader[R, B]): Reader[R, B] =
Reader { r: R =>
f(run(r)).run(r)
}
}

The Reader type is just a wrapper over a function which takes a context of type R and returns some result of type A. The flatMap combines two run functions togetherwe're doing this by calling run with a given context, applying the given transformation to the result, and then calling the run for the result. The first call of the run is basically for this, while the second is for the Reader we're getting by applying f.

We can also define a constructor for some value that ignores any given context:

object Reader {
def apply[R, A](a: => A): Reader[R, A] = Reader(_ => a)
}

Now that we have this model, we can have a monad for it, just like we did with the state monadby using the kind-projector syntax:

implicit def readerMonad[R] = new Monad[Reader[R, ?]] {
override def unit[A](a: => A): Reader[R, A] = Reader(a)
override def flatMap[A, B](a: Reader[R, A])(f: A => Reader[R, B]): Reader[R, B] = a.compose(f)
}

Unsurprisingly, the monad just delegates to both the constructor and the compose method we just defined. Surprisingly, now that we've done this, we're done defining the reader monad and can use it with our definition of the move function!

Let's imagine that we have a regulation that defines a speed limit for boats and the maximal angle they are allowed to turn at once (sounds strange, but in the place, we're fishing we have case law, so this is what we've got).

As this is external regulation, we have to model it with a case class:

final case class Limits(speed: Float, angle: Double)
type ReaderLimits[A] = ch09.Reader[Limits, A]

We'll also define an alias fixes the type of context for a Reader to be Limits.

Now, we can redefine our go and turn methods by applying these limits, like so:

def go(speed: Float, time: Float)(boat: Boat): ReaderLimits[Boat] =
ch09.Reader(limits => {
val lowSpeed = Math.min(speed, limits.speed)
boat.go(lowSpeed, time)
})

def turn(angle: Double)(boat: Boat): ReaderLimits[Boat] =
ch09.Reader(limits => {
val smallAngle = Math.min(angle, limits.angle)
boat.turn(smallAngle)
})

There is nothing special about the implementation itself. The type signature of functions are predefined by the move method. After each action, we return Reader[Limits, Boat]. To calculate the new state of the boat, we delegate to its methods after figuring out the maximal speed or angle we can apply. 

As we designed the rest of the code in a generic way, this is all we need to do—Let's move:

import Monad.readerMonad
import Boat._
println(move(go, turn)(ch09.Reader(boat)).run(Limits(10f, 0.1)))
Boat(0.0,(250.00083305560517,19.96668332936563))

To run this example, please use the SBT run command.

We're passing the go and turn functions we just defined to the generic move method, along with the properly wrapped boat, and run it afterward. By looking at the result, we can say that the speed limits were properly applied.

After scrutinizing the state monad, there is not much left to discuss the reader, so we're good to proceed to the Writer monad.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.178.9