Page 15 - HRM-00-v1
P. 15
The dry-rb 1 collection of gems is a great example of this approach, and in this article, we’ll explore how dry-monads can help with model- ing complex data transformations robustly.
The M-Word: Monad Basics
In many programming circles, monads are seen as arcane constructs that are only of interest to academics. This is unfortunate since they are not all that complicated: essentially monads are just a way to per- form a series of computations within a “context.” To do this, a “plain” value like a string or integer is first “wrapped” by a function called return in FP jargon. These wrapped values are then combined with an operation called bind, which removes their wrapping, performs the desired operation on their underlying value, and rewraps the re- sult. In the end, the context is removed again, often through a concept called pattern-matching 2.
dry-monad in Action
All of this may still sound too theoretical, so let’s look at the Maybe 3 monad as a concrete example. Maybe’s purpose is to model a series of computations that could return nil at any intermediate step. In- stead of mixing business logic with repeated error checks, we “wrap” our starting value in a Maybe, perform all our operations, and only check the result at the very end, where it will either be of the form Some(value) when everything went according to plan, or None when a nil was encountered.
Article by Michael Kohl WMain illustration by Sergey Konotopcev
LIBRARIES
Functional Ruby with `dry-monad`s
HILE RUBY MAY NOT BE THE COOL NEW KID ON THE BLOCK ANYMORE,
THERE’S BARELY BEEN
A BETTER TIME TO BE A RUBYIST. THIS IS NOT ONLY DUE TO CONSTANT LANGUAGE IMPROVEMENTS, BUT
ALSO BECAUSE OF A NEW GENERATION OF GEMS THAT ARE FRAMEWORK AGNOSTIC AND ARE DESIGNED AROUND PLAIN OLD RUBY OBJECTS (PORO).
In a web application this could, for example, be used to return the up- percase version of a user’s name, or the default value “ANONYMOUS USER” if there’s no currently logged-in user or the name isn’t set. Let’s look at this example step by step. First, we require the Maybe monad and alias the Dry::Monads module to M to save ourselves some typing. We also set up a dummy user:
require ‘dry/monads/maybe’ M = Dry::Monads
current_user = nil
Our maybe_name function first “wraps” the user in a Maybe context and uses the bind method to apply a block to this monadic value. Inside the block, we try to access the user’s name and then repeat the same process to finally call upcase on it:
def maybe_name(user) M.Maybe(user).bind do |u|
M.Maybe(u.name).bind do |n| M.Maybe(n.upcase)
end end
end
maybe_name(current_user)
#=> None
15 | Human Readable Magazine