safety wrapper
Monads are notoriously hard to explain, and I don’t claim to know the first thing about their underlying concepts from category theory.
But it is a concept I want to learn more about. I started with this video and decided to implement some of the concepts I saw.
One use case that I sorta understand is using a monad as a “wrapper” around unsafe values. For example, values that result from making external system calls via a database connection or REST api. Scala provides excellent native support for this approach, for example the Either
, Try
and Option
types. Wrapping unsafe operations in these types can lead to clean, readable code when used properly.
For this example let’s implement a basic Maybe
style monad. This Maybe
type is a wrapper around another other type, which we refer to a A
. Here’s the interface:
The key methods here are map
and flatMap
. Here’s the difference:
map
- takes a regular function,
- executes the function,
- wraps the value in a Maybe and returns it
def map[B](f: A => B): Maybe[B] = new Maybe(f(a))
flatMap
- takes a function that returns a Maybe and executes it
def flatMap[B](f: A => Maybe[B]): Maybe[B] = f(a)
Our two implementations of Maybe
:
The major win here is we get a stable API interface, where we can call map
and flatMap
all day long and not “throw” any errors and end up with a nasty user experience. We can wrap our errors and pass them thru a computation chain and then deal with the result cleanly.
–
Let’s see it in action. First here are some stand-in operations to simulate our external calls.
The key here is that our 2 services return objects that implement the Maybe
API.
In action (for comprehension
style):
(as a reminder, for comprehension
s represent calls to flatMap
until the final statement which is a call to map
)
Even though the 2nd call simulateFailedSearch
failed, the code block executes safely and yields a NotPresent
which can be dealt with by the caller.
–
In action (function chain style):
Our call to getOrElse
lets us know the function chain encountered a NotPresent
, but still completed safely.
One more example. Let’s use the map
function to try some regular Scala string operations, and see what happens.
As expected, the call to toUpperCase
succeeded and we got our uppercase result from the REST call, wrapped in a Maybe
.
Now let’s do the same operation but with a failure in the middle:
Since calling map
on a Maybe
is always supported, our failure simply passes thru the call chain and pops out the bottom.
Honestly in the case of external API calls and such, an Either
type would be better here as the exact nature of the failure can be passed thru the function chain instead of merely a NotPresent
. I think of an Either
as the same as a Maybe
monad but with more context.
This has been an extremely basic introduction to monads in Scala.