<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://bridgetownrb.com/" version="2.1.2">Bridgetown</generator><link href="https://morozov.is/feed.xml" rel="self" type="application/atom+xml" /><link href="https://morozov.is/" rel="alternate" type="text/html" /><updated>2026-05-01T06:35:54+00:00</updated><id>https://morozov.is/feed.xml</id><title type="html">Igor Morozov</title><subtitle>A blog about building and scaling software: Ruby, Rails, architecture, testing, and developer experience — practical guides, opinions, and real lessons.</subtitle><entry><title type="html">Choosing a Service Object Design in Ruby</title><link href="https://morozov.is/2020/06/01/helpful-service-objects-part-1-chosing-right-design" rel="alternate" type="text/html" title="Choosing a Service Object Design in Ruby" /><published>2020-06-01T18:57:00+00:00</published><updated>2020-06-01T18:57:00+00:00</updated><id>repo://posts.collection/_posts/2020-06-01-helpful-service-objects-part-1-chosing-right-design.md</id><content type="html" xml:base="https://morozov.is/2020/06/01/helpful-service-objects-part-1-chosing-right-design">&lt;p&gt;I’ve been programming for a long time and I’ve had countless arguments about different things. I’d like to list top four reasons I’ve had an argument online.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Style guide.&lt;/strong&gt; Thankfully, the number of arguments reduces as I mature, but I’m still spending a lot of time on them. I’d rather have an extremely opinionated styleguide and just stop talking about it. Something like &lt;a href=&quot;https://github.com/wemake-services/wemake-python-styleguide&quot;&gt;wemake-python-styleguide&lt;/a&gt;, but for Ruby.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monads.&lt;/strong&gt; I have to admit that this word is almost banned from my vocabulary because of how many arguments I’ve had about it. It’s getting better, but people still like to argue about them. I &lt;a href=&quot;/2020/04/01/should-i-really-use-monads.html&quot;&gt;wrote an article recently&lt;/a&gt; about them in hope to show that there’s nothing special to argue about – monads are just abstractions that may or may not be helpful. It all depends on your problems and approaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to design domain logic.&lt;/strong&gt; It may be an extremely interesting and helpful discussion, or it may turn into a useless argument. When it goes bad, it’s usually because we’re trying to discuss insignificant details and lower-level things. Where do we put arguments? What about dependency injection? How do we use instance variables? Fat model? Service objects? Ughh!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Different interpretation of common terminology.&lt;/strong&gt; What do we mean when we say “interactor”? What about “architecture”? Is it a &lt;em&gt;state&lt;/em&gt; when we’re just passing values from function to function? Is duck typing &lt;em&gt;really&lt;/em&gt; an absence of types? What is a type, anyway? What does it mean to write “object oriented” code? What about “functional” approach? Do we need &lt;a href=&quot;https://www.yegor256.com/2014/06/09/objects-should-be-immutable.html&quot;&gt;immutability in OO design&lt;/a&gt;? Those topics lead to endless discussions with little output.&lt;/p&gt;

&lt;p&gt;As much as I love learning about new things, those arguments are extremely energy-draining. They got me thinking: since we’re usually going over the same thing, why don’t we just dump the knowledge somewhere and refer to it instead of arguing? That’s what I’m going to do.&lt;/p&gt;

&lt;p&gt;I’m starting a series of blog posts about different topics in Ruby world. My goal is to describe different approaches to the same problems and highlight pros and cons of each one. Perhaps, pick a favorite one and promote it.&lt;/p&gt;

&lt;p&gt;Right now I want to focus on two larger topics:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Designing service objects&lt;/li&gt;
  &lt;li&gt;Handling errors in domain logic. Exceptions, values, result objects&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a first post of the series, and it will cover the first topic: building helpful service objects.&lt;/p&gt;

&lt;p&gt;We will go through the basics: what are we talking about when we say “service object”. We’ll look through different approaches and see which ones bring the most benefit and which ones should probably be put to rest. In the end, I’m going to suggest a working design and a couple of guidelines you can use to improve your service object game.&lt;/p&gt;

&lt;!-- excerpt --&gt;

&lt;h1 id=&quot;our-main-challenge-is-domain-logic&quot;&gt;Our main challenge is domain logic&lt;/h1&gt;

&lt;p&gt;Service object is a common pattern in Ruby community, but you might also see something similar in other languages. Python’s &lt;a href=&quot;https://github.com/dry-python/stories&quot;&gt;stories&lt;/a&gt; were greatly influenced by &lt;a href=&quot;http://github.com/gems/dry-transactions&quot;&gt;dry-transactions&lt;/a&gt; and &lt;a href=&quot;http://trailblazer.to/&quot;&gt;Trailblazer&lt;/a&gt; — some of the tools we could have used for service objects.&lt;/p&gt;

&lt;p&gt;The sole purpose of a service object is to be a place for your domain logic. Remember the usual models vs controllers dilemma? Well, think no more. Complex domain logic goes to service objects.&lt;/p&gt;

&lt;p&gt;Let’s step back a little and see why we need to care about the logic at all, and why we should care about its exact location.&lt;/p&gt;

&lt;p&gt;Usually, we’re building applications that solve a set of problems for a specific business. Sometimes its logic is trivial, but usually it’s something a sophisticated system that backs a complex business. Sometimes the software &lt;em&gt;is&lt;/em&gt; the product that we sell. Either way, the logic becomes complex quite easily.&lt;/p&gt;

&lt;p&gt;When we’re modelling complex processes, we have to make a lot of decisions: what needs to be captured in the model; what are the names and processes; what are the boundaries; what’s the shape of our data; how to organize domain logic &lt;em&gt;well&lt;/em&gt;. Service objects don’t answer all of our questions, but they &lt;em&gt;nudge&lt;/em&gt; us.&lt;/p&gt;

&lt;h1 id=&quot;service-objects-nudge-us-to-organize&quot;&gt;Service objects nudge us to organize&lt;/h1&gt;

&lt;p&gt;When we were working with the common model-view-controller paradigm, we had to make trade-offs and try and design logic using what we’ve got: models and controllers. Concerns, if we’re advanced enough. We put domain logic in the models, application logic in the controllers, and we get our happily ever after.&lt;/p&gt;

&lt;p&gt;What if the logic doesn’t fit the model, though? It happens when the relationship is not obvious, or when the process affects multiple models. Here’s a few examples where the solution is not as obvious:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Does an applicant get a job at a company, or does the company hire the applicant?&lt;/li&gt;
  &lt;li&gt;Does a buyer sell the goods, or does the customer buy the goods?&lt;/li&gt;
  &lt;li&gt;What if we’re trying to match people by their taste in music? How do we measure it? Is it &lt;code class=&quot;highlighter-rouge&quot;&gt;jane#compare(john)&lt;/code&gt;, or is it the other way around? Is it something else?&lt;/li&gt;
  &lt;li&gt;What if we’re firing someone? Does a manager fire the person? Does the person fire themself? What if HR initiated the process, not the manager? What if it’s a layoff?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes, the processes in our business are a bit more complex to be &lt;em&gt;reasonably&lt;/em&gt; put into a specific model. There are usually different solutions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add multiple entry points&lt;/strong&gt; like &lt;code class=&quot;highlighter-rouge&quot;&gt;Group#add&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;User#add_to_group&lt;/code&gt;. Works best if you’ve found a way to avoid duplication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a new model.&lt;/strong&gt; It works best if the model matches the real-world domain. It’s reasonable to have a &lt;code class=&quot;highlighter-rouge&quot;&gt;JobApplication&lt;/code&gt; which can be accepted or rejected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use different models in different contexts.&lt;/strong&gt; It’s easier to make decisions when we contextualize things. This way, similar entities have different behavior, depending on the context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extract the logic&lt;/strong&gt; into a function / procedure. It feels quite natural to have an option to call &lt;code class=&quot;highlighter-rouge&quot;&gt;hire_candidate&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;rounded-md bg-blue-50 p-4&quot;&gt;
  &lt;div class=&quot;flex&quot;&gt;
    &lt;div class=&quot;flex-shrink-0&quot;&gt;
      &lt;svg class=&quot;h-5 w-5 text-blue-400&quot; viewBox=&quot;0 0 20 20&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&gt;
        &lt;path fill-rule=&quot;evenodd&quot; d=&quot;M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z&quot; clip-rule=&quot;evenodd&quot; /&gt;
      &lt;/svg&gt;
    &lt;/div&gt;
    &lt;div class=&quot;ml-3 flex-1 md:flex md:justify-between&quot;&gt;
      &lt;p class=&quot;text-sm text-blue-700 not-prose&quot;&gt;
        &lt;strong&gt;Note:&lt;/strong&gt; I&apos;ve been saying &quot;model&quot; quite a lot. It&apos;s not always about ActiveRecord::Model, though. It can be a plain Ruby object or Sequel::Model too.
      &lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Service objects nudge us to to the latter – extract the logic into a function or a procedure. Except, we’re using classes and objects instead of “real” functions. Hence the name “service objects”.&lt;/p&gt;

&lt;p&gt;It’s not a silver bullet by any chance, but it’s a nice tool which you can combine with other approaches to build better software.&lt;/p&gt;

&lt;h1 id=&quot;what-they-look-like&quot;&gt;What they look like&lt;/h1&gt;

&lt;p&gt;Here’s the thing about service object: it’s not really a well-documented pattern. People try to figure out how to design them in a meaningful way, and they get different results. There’s a lot of ways to categorize the service objects, with different level of detail. I’m going to take a shot and categorize by the service object behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The doer&lt;/strong&gt; is an service object which has the &lt;code class=&quot;highlighter-rouge&quot;&gt;-er&lt;/code&gt; suffix in the name. It has a name like &lt;code class=&quot;highlighter-rouge&quot;&gt;OrderCreator&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;UserRenamer&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;PurchasePlacer&lt;/code&gt; or something similar. This object looks like it’s a person fulfilling their job, and usually doesn’t exist in anyone’s vocabulary, except for the developers. In some cases, it may clash with the terminology as &lt;code class=&quot;highlighter-rouge&quot;&gt;OrderCreator&lt;/code&gt; may be both a person who placed the order &lt;em&gt;and&lt;/em&gt; a service object. The doer usually has one public method named &lt;code class=&quot;highlighter-rouge&quot;&gt;call&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;perform&lt;/code&gt;, or the one matching its purpose: &lt;code class=&quot;highlighter-rouge&quot;&gt;create&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;update&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;assign&lt;/code&gt;, etc.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderCreator&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# or call / perform / etc&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The multitool&lt;/strong&gt; is a service which fulfills many jobs at once. For instance, &lt;code class=&quot;highlighter-rouge&quot;&gt;OrderManager&lt;/code&gt; may assign and remove couriers, update delivery dates and even cancel the order. Everything is centered around a specific entity.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderManager&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reschedule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The event&lt;/strong&gt; is a service object which models a process which starts when &lt;em&gt;a specific event&lt;/em&gt; occurs. It’s usually a complex multi-step process. The name usually captures the name of the event: &lt;code class=&quot;highlighter-rouge&quot;&gt;ApplicationSubmitted&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;OrderShipped&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;UserBlocked&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;OrderPlaced&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# or handle / perform / etc&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The command&lt;/strong&gt; is a lot similar to &lt;em&gt;the event&lt;/em&gt;, except it’s designed as an imperative action in your domain. The object has a name similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;SubmitApplication&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;SubmitOrder&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;BlockUser&lt;/code&gt;, etc. It may look like &lt;em&gt;the doer&lt;/em&gt; except for one major difference: it has a proper naming. People also call it an “operation”, or even a “use case” or an “interactor”.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SubmitOrder&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# or handle / perform / etc&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Other service object implementations usually fit within one of those four groups. If I’ve missed out on something, please let me know at &lt;a href=&quot;mailto:igor@morozov.is&quot;&gt;igor@morozov.is&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m going to be blunt and say that you should probably throw away &lt;em&gt;the doer&lt;/em&gt; and &lt;em&gt;the multitool&lt;/em&gt; and replace them with something else if you’re using them. Let’s see what their problem is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The doer&lt;/strong&gt; is like &lt;em&gt;the command&lt;/em&gt;, except it has a poor naming. Instead of capturing something real like an action, it’s modelled as if it’s a full-grown entity in your business. In most cases your business does &lt;em&gt;not&lt;/em&gt; have a &lt;code class=&quot;highlighter-rouge&quot;&gt;SomethingCreator&lt;/code&gt;, but it has an process to &lt;code class=&quot;highlighter-rouge&quot;&gt;CreateSomething&lt;/code&gt;. There’s a huge benefit in speaking a natural language instead of inventing your own, so my advise is &lt;strong&gt;turn all doers into commands&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The multitool&lt;/strong&gt; is a nice attempt at a service object, but has a couple of fundamental flaws.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Similar to &lt;em&gt;the doer&lt;/em&gt;, the name doesn’t capture domain pretty well. I’ve seen people choose names like &lt;code class=&quot;highlighter-rouge&quot;&gt;OrderManager&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;OrderService&lt;/code&gt;. Neither of those really exists in the domain.&lt;/li&gt;
  &lt;li&gt;Is it really a “service”, or is it a model in disguise? You may achieve similar level of isolation by extracting the logic to a module / a concern and including it to your model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I can totally understand the desire to use this design because it extracts and isolates the logic and makes it &lt;em&gt;feel&lt;/em&gt; like everything is better, I’d advise everyone to take a deeper look at their own paradigm and see if there are better tools to solve the same problems.&lt;/p&gt;

&lt;p&gt;One of the question you have to ask ourselves: why did we have to stray away from the good old object-oriented model and common Rails ways? Perhaps, we’re better off using approaches described in Eventide’s &lt;a href=&quot;http://docs.eventide-project.org/user-guide/useful-objects.html#overview&quot;&gt;useful object&lt;/a&gt; manifesto, Yegor Bugaenko’s &lt;a href=&quot;https://www.yegor256.com/elegant-objects.html&quot;&gt;Elegant Objects&lt;/a&gt; or Ivan Nemytchenko’s &lt;a href=&quot;https://railshurts.com/&quot;&gt;Rails Hurts → Painless Rails&lt;/a&gt;. I’m no expert in any of those things, so let’s speak about useful &lt;em&gt;service&lt;/em&gt; objects instead.&lt;/p&gt;

&lt;h1 id=&quot;why-some-service-objects-are-more-useful-than-others&quot;&gt;Why some service objects are more useful than others&lt;/h1&gt;

&lt;p&gt;In his RubyRussia 2019 talk &lt;a href=&quot;https://www.youtube.com/watch?v=DfU6H-8qal8&quot;&gt;“The future of dependency management in Ruby”&lt;/a&gt; Anton Davydov mentioned the problems with service objects and showed the many ways to use them. When he mentioned the lack of standardization, I knew I wanted to write an overview and highlight the most useful ones, so let’s do it.&lt;/p&gt;

&lt;figure class=&quot;&quot;&gt;
  &lt;img src=&quot;/images/posts/service_objects/anton_name.png&quot; alt=&quot;A slide from Anton&apos;s talk depicting at least 8 ways to name service object&apos;s method&quot; /&gt;&lt;figcaption&gt;
      Oh the diversity of methods

    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;As you can see, there are at least eight popular ways to name the service object’s primary method. It is not really a problem, as you just have to pick whatever works for you and stay consistent about it. I prefer &lt;code class=&quot;highlighter-rouge&quot;&gt;#call&lt;/code&gt;, but you might want something else.&lt;/p&gt;

&lt;p&gt;There’s a deeper problem: how do we actually use the objects? How do we build them? Where do we pass the parameters? What about configuration? What about dependencies? Oh my! Just take a look at the many ways to use service objects. The slide covers probably 99% of known service object usages, so kudos to Anton for putting together the list.&lt;/p&gt;

&lt;figure class=&quot;&quot;&gt;
  &lt;img src=&quot;/images/posts/service_objects/anton_use.png&quot; alt=&quot;A slide from Anton&apos;s talk depicting at least 5 ways to use service objects&quot; /&gt;&lt;figcaption&gt;
      The problematic variety of ways

    &lt;/figcaption&gt;&lt;/figure&gt;

&lt;p&gt;Let’s make sure we’re on the same page about terminology before going on.&lt;/p&gt;

&lt;p&gt;When we’re talking about &lt;code class=&quot;highlighter-rouge&quot;&gt;params&lt;/code&gt;, we’re usually talking about ordinary data that we pass to the object. It’s the same arguments we would normally pass to a method that does something.&lt;/p&gt;

&lt;p&gt;Dependencies are a bit more tricky. Usually, our service objects can’t perform a task on their own. They need to know how to retrieve data from the database, how to send an e-mail, how to run some related logic. It’s impractical to implement all of this ourselves, so we &lt;em&gt;delegate&lt;/em&gt; it to some external objects, services and modules. &lt;em&gt;Those&lt;/em&gt; are the dependencies. They’re the owners of the knowledge.&lt;/p&gt;

&lt;p&gt;Options are a bit like dependencies, but simpler. It’s a run-time configuration. Rule of thumb: if you’ve put some magic numbers, strings or other values in a constant, it’s likely one of &lt;em&gt;those&lt;/em&gt; options.&lt;/p&gt;

&lt;p&gt;Now that we’re clear about shared terminology, let’s speak about the list. I’ve rearranged it and split the items in three groups. The result is heavily opinionated, so I’ll explain it afterwards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The most helpful&lt;/strong&gt; service objects are the ones which give you the most power. They’re arguably the most pragmatic ones.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Service.new(options).call(params)&lt;/li&gt;
  &lt;li&gt;Service.new(dependencies).call(params), which is almost the same as the example above&lt;/li&gt;
  &lt;li&gt;Service.new.call(params), &lt;em&gt;only if&lt;/em&gt; it’s a shorthand for the first two options with reasonable defaults&lt;/li&gt;
  &lt;li&gt;Service.call(params), when it’s an instance created via the first three options. i.e. &lt;code class=&quot;highlighter-rouge&quot;&gt;Service = OtherService.new(...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Moderately helpful&lt;/strong&gt; won’t bring you as much benefit, but they’re still decent if you use them well&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Service.call(params), when you just don’t need to instantiate anything. You won’t get the benefit of configuration, dependency injection or anything, but it’s still a decent piece of logic&lt;/li&gt;
  &lt;li&gt;Service.new.call(params). It’s not really helpful if you cant’t configure it at all, but oh well. A future-proof design may be helpful though.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Not really helpful&lt;/strong&gt; are redundant or just poorly designed. You should probably reconsider when you meet one&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Service.new(params).call&lt;/li&gt;
  &lt;li&gt;Service.call(params) is bad if it’s just a shorthand for most of the &lt;code class=&quot;highlighter-rouge&quot;&gt;new.call&lt;/code&gt; variations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This classification is purely opinion-based, yet there’s reasoning behind all this. It’s mostly based on my own experience in software engineering, and a couple of other ideas. It mostly comes from the fact that I like my code to be deterministic and easily modifiable. I’m also a little product-oriented, so I fiddle around with different configuration quite often.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each object must have a reasonable lifetime.&lt;/strong&gt; Service objects are essentially complex functions and procedures, and their lifetime should &lt;em&gt;probably&lt;/em&gt; be similar to one of any other function, module or class. Even if we’re into OOP, instantiating and object which can only be used once before being discarded seems to go against the general idea. Sure, there are cases where the function lifetime should be short, but those cases require extra thought.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logic should be easily extendable.&lt;/strong&gt; Especially if we’re building a start-up which is rapidly evolving. Want to pay your contractors a 10% bonus instead of a usual 5%? Just configure the service and use it. Handy for rapid and cheap experimenting. Want to refund a user &lt;em&gt;even though we normally don’t&lt;/em&gt;? Just use the service with a different set of policies. Works best if I don’t have to write any code to customize it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code should expose bad design&lt;/strong&gt; instead of promoting it. Writing an overly complex logic should be possible, yet the code must &lt;em&gt;look and feel&lt;/em&gt; overly complex. This way, you’ll be able to improve your design before it becomes too time-consuming to maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No mutable state&lt;/strong&gt; is a common idea in functional programming and a &lt;a href=&quot;https://www.yegor256.com/2014/06/09/objects-should-be-immutable.html&quot;&gt;not-that-common idea in the OOP world&lt;/a&gt;. It adds verbosity, but &lt;strong&gt;verbosity is not a problem&lt;/strong&gt;. We’ll get better reusability, testability and composability if we follow the rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Services should be composable&lt;/strong&gt;. It means we should be able to organize them in a nice pipeline to avoid clumsy interfaces. We can achieve it by returning composable values, like result objects, monads and stuff like this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our logic should be insighful.&lt;/strong&gt; The code should help us figure out how the world works. We need to learn about our processes, their limitations and core participants. The complexity of the process, points of pressure, possible bugs and likely mismatch with the real-world domain. The code should help us gather the insights instead of obfsucating them.&lt;/p&gt;

&lt;p&gt;That said, I’ve found that I get the most benefit when I’m using a constructor to configure the service object and provide dependencies, and pass the input parameters to the &lt;code class=&quot;highlighter-rouge&quot;&gt;#call&lt;/code&gt; itself. It’s a bit more verbose because I have to explicitly declare all dependencies and I have to pass variables around. It brings a great benefit as I can &lt;em&gt;feel&lt;/em&gt; that I have to refactor this place when it gets too complex. I also heavily use default dependencies, so I don’t have to be &lt;em&gt;too&lt;/em&gt; explicit.&lt;/p&gt;

&lt;p&gt;Whenever I feel like there’s no need to configure, or when the team has a different convention, I like to use class methods and avoid instances. This way, I’m still getting the benefits of a good lifetime &lt;em&gt;and&lt;/em&gt; I get to expose the overly complex design. This works pretty well too.&lt;/p&gt;

&lt;div class=&quot;rounded-md bg-blue-50 p-4&quot;&gt;
  &lt;div class=&quot;flex&quot;&gt;
    &lt;div class=&quot;flex-shrink-0&quot;&gt;
      &lt;svg class=&quot;h-5 w-5 text-blue-400&quot; viewBox=&quot;0 0 20 20&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&gt;
        &lt;path fill-rule=&quot;evenodd&quot; d=&quot;M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z&quot; clip-rule=&quot;evenodd&quot; /&gt;
      &lt;/svg&gt;
    &lt;/div&gt;
    &lt;div class=&quot;ml-3 flex-1 md:flex md:justify-between&quot;&gt;
      &lt;p class=&quot;text-sm text-blue-700 not-prose&quot;&gt;
        &lt;strong&gt;Q:&lt;/strong&gt; why use class methods when you can just use a module?
      &lt;/p&gt;
      &lt;p class=&quot;text-sm text-blue-700 not-prose&quot;&gt;
        &lt;strong&gt;A:&lt;/strong&gt; I like to think that modules are meant to be included into your classes, or serve as a namespace. There&apos;s nothing wrong with using module methods instead, though.
      &lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Other designs, especially the &lt;code class=&quot;highlighter-rouge&quot;&gt;new(params).call&lt;/code&gt; have failed to meet my expectations. Its only benefit is that I can utilize instance variables to save myself a few taps. I don’t want to trade off all the benefits for that.&lt;/p&gt;

&lt;p&gt;I’ll stick to the &lt;code class=&quot;highlighter-rouge&quot;&gt;new(options/dependencies).call(params)&lt;/code&gt;, as this is the most powerful way to use service objects. We’re going to dive deeper into the practice in the next part, so here’s the design that I’m promoting:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# A service object which pays a baker a bonus for an order&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# It&apos;s a command, or an operation&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RewardBaker&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:bonus_ratio&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;bonus_ratio: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@bonus_ratio&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bonus_ratio&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# &amp;lt;= any other dependency / configuration goes here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;= logic goes here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;reward_baker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RewardBaker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;bonus_ratio: &lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.05&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# a 5% bonus is nice&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;reward_baker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;other_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;whats-going-to-happen-next&quot;&gt;What’s going to happen next&lt;/h1&gt;

&lt;p&gt;Service objects are a large enough topic, and I can’t cover them in one post. If I do, only a few people will have the time to read it – it’s going to be too overwhelming. So I’m going to release at least two more parts: “the practice” and “the next level”.&lt;/p&gt;

&lt;p&gt;The practice will be a design exercise where we model a business process and illustrate decisions and trade-offs of service objects. It’s going to show how to use the event and the command and why those designs have a name which looks like they come from CQRS, Event-Sourcing or something similar.&lt;/p&gt;

&lt;p&gt;In “The next level” I’ll talk about techniques which will help you get more from service objects: reduce boilerplate, organize a pipeline, and gather more insights. Afterwards I’ll address some of the flaws. I’ll finish it with a small guideline on designing service objects. I’ll also address some of my claims about the design decisions.&lt;/p&gt;

&lt;h1 id=&quot;the-rabbit-hole&quot;&gt;The rabbit hole&lt;/h1&gt;

&lt;p&gt;If you want to go down the rabbit hole and discover more yourself, feel free to dig through the resources I’ve mentioned below. Make sure to check the first two articles. They criticize service objects and provide nice alternatives — those may be extremely helpful for you too.&lt;/p&gt;

&lt;p&gt;Avdi Grimm &lt;a href=&quot;https://avdi.codes/service-objects/&quot;&gt;highlights the possible problems&lt;/a&gt; of service objects and provides alternatives. The “Domain-driven design” part is important, you should totally read it.&lt;/p&gt;

&lt;p&gt;Jason Swett wrote a &lt;a href=&quot;https://www.codewithjason.com/rails-service-objects/&quot;&gt;nice piece&lt;/a&gt; addressing service objects and their problem.&lt;/p&gt;

&lt;p&gt;Toptal has a &lt;a href=&quot;https://www.toptal.com/ruby-on-rails/rails-service-objects-tutorial&quot;&gt;solid article on service objects&lt;/a&gt; which gives some advise on good designs. You may notice that I don’t agree with it, but it’s alright. You may like their reasoning more. Check it out.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://railshurts.com/&quot;&gt;Ivan Nemytchenko’s book on Painless Rails&lt;/a&gt;. If you like Rails, but not quite, this book might help you improve your game without service objects.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=DfU6H-8qal8&quot;&gt;The future of dependency management in Ruby&lt;/a&gt; by Anton Davydov&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://docs.eventide-project.org/user-guide/useful-objects.html#overview&quot;&gt;Useful objects&lt;/a&gt; manifesto by eventide&lt;/p&gt;

&lt;p&gt;Hanami guide has a &lt;a href=&quot;https://guides.hanamirb.org/architecture/interactors/&quot;&gt;decent article on the pattern&lt;/a&gt; and how to use it.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/gems/dry-transactions&quot;&gt;dry-transactions&lt;/a&gt; — a once-popular gem, which used to be deprecated, but right now it’s going throw a rework. It’s a DSL for domain logic, which essentially implements the “service object” and “railway oriented programming” ideas in a nice way&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://trailblazer.to/&quot;&gt;Trailblazer&lt;/a&gt; was aimed to simplify our lives and domain logic, and brought a lot of new ideas to Ruby world&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/dry-python/stories&quot;&gt;How Python devs implemented the idea&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/wemake-services/wemake-python-styleguide&quot;&gt;Most strict linter for Python&lt;/a&gt;. I want something like this in Ruby. Hopefully, &lt;a href=&quot;https://github.com/testdouble/standard&quot;&gt;Standard&lt;/a&gt; will help with that.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.yegor256.com/2014/06/09/objects-should-be-immutable.html&quot;&gt;Yegor Bugaenko’s blog&lt;/a&gt; contains a lot of controversial and thought-provoking content. One of the ideas is that immutable objects should be a default in the object-oriented paradigm.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.yegor256.com/elegant-objects.html&quot;&gt;Yegor Bugaenko wrote a book&lt;/a&gt; on his ideas. If you want a different look on OOP, it’s definitely going to help you.&lt;/p&gt;

&lt;p&gt;I wrote &lt;a href=&quot;/2020/04/01/should-i-really-use-monads.html&quot;&gt;Should I really use monads?&lt;/a&gt; to discuss a smaller topic which comes with service objects: monads. Together those two abstractions enable you to do railway oriented programming, which is nice.&lt;/p&gt;

&lt;p&gt;Rob Race’s article on &lt;a href=&quot;https://hackernoon.com/the-3-tenets-of-service-objects-c936b891b3c2&quot;&gt;3 tenets of service objects&lt;/a&gt;. It was an interesting read.&lt;/p&gt;

&lt;p&gt;Scott Domes shows the &lt;code class=&quot;highlighter-rouge&quot;&gt;ServiceObject.call(args)&lt;/code&gt; as a shorthand for &lt;code class=&quot;highlighter-rouge&quot;&gt;new(args).perform&lt;/code&gt; in &lt;a href=&quot;https://medium.com/@scottdomes/service-objects-in-rails-75ca74214b77&quot;&gt;Service objects in Rails&lt;/a&gt;.&lt;/p&gt;</content><author><name></name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://morozov.is/images/posts/service_objects/part_one.png" /><media:content medium="image" url="https://morozov.is/images/posts/service_objects/part_one.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Should I _really_ use monads?</title><link href="https://morozov.is/2020/04/01/should-i-really-use-monads" rel="alternate" type="text/html" title="Should I _really_ use monads?" /><published>2020-04-01T20:19:00+00:00</published><updated>2020-04-01T20:19:00+00:00</updated><id>repo://posts.collection/_posts/2020-04-01-should-i-really-use-monads.md</id><content type="html" xml:base="https://morozov.is/2020/04/01/should-i-really-use-monads">&lt;p&gt;A couple of weeks ago I witnessed a dialogue in a &lt;a href=&quot;https://t.me/rubylang&quot;&gt;Ruby chat&lt;/a&gt;. I’m paraphrasing, but it went like this:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;xxx: What is &lt;code class=&quot;highlighter-rouge&quot;&gt;dry&lt;/code&gt;? I’ve seen this gem prefix and discussions, but never actually learned about it. &lt;br /&gt;
yyy: It’s a set of libraries to tackle some problems. &lt;br /&gt;
zzz: Yeah, and introduce new ones, such as “How do I explain to my colleague that they need monads”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s be honest. I felt so many emotions that I couldn’t think straight. I’ve been discussing this exact topic so many times that I’ve exhausted myself. There’s a lot of misconceptions, frustration and plain skepticism around monads, and it all leads to aggressive rejection by many.&lt;/p&gt;

&lt;p&gt;Right now, I want to finish this topic once and for all. Not going to do it in this post, though. I’m writing a huge piece on error handling techniques in Ruby, which will cover strong and weak points of different techniques, including monads.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;this&lt;/em&gt; post, I will try to step back and speak about monads from a more practical and emotional perspective. I will briefly explain what a monad really is, why is it valuable, and some of the common issues with it. It’s about people and technology, so don’t expect to see any code.
&lt;!-- excerpt --&gt;&lt;/p&gt;

&lt;h1 id=&quot;what-is-a-monad&quot;&gt;What is a monad&lt;/h1&gt;

&lt;p&gt;When we’re speaking about monads in Ruby, we’re usually talking about &lt;a href=&quot;https://dry-rb.org/gems/dry-monads/&quot;&gt;dry-monads&lt;/a&gt; – a library that implements them. However, monad is a much broader concept that comes from category theory:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A monad is just a &lt;em&gt;monoid&lt;/em&gt; in the category of endofunctors. What’s the problem?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The internet is full of jokes like this. It’s a comprehensive definition, but it’s &lt;em&gt;so vague&lt;/em&gt;. To be fair, &lt;em&gt;most&lt;/em&gt; definitions won’t give you anything concrete. Giving a definition to monads is a lot like trying to give a proper definition to a “musical instrument”. It’s going to be either vague and correct or understandable and incomplete; simply because each instrument, just like each monad, plays a different role. I’ll give you somewhat correct, but a little vague explanation.&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;highlighter-rouge&quot;&gt;monad&lt;/code&gt; is just a &lt;del&gt;fancy&lt;/del&gt; mathematical name for abstractions that behave in a &lt;a href=&quot;/2018/09/08/monad-laws-in-ruby.html&quot;&gt;specific way&lt;/a&gt;. In theory, it lets us chain them, compose them in different ways. In practice, we’d rarely notice the monadic nature of those abstractions. Let’s focus on individual monads and see what they bring to the table.&lt;/p&gt;

&lt;div class=&quot;rounded-md bg-blue-50 p-4&quot;&gt;
  &lt;div class=&quot;flex&quot;&gt;
    &lt;div class=&quot;flex-shrink-0&quot;&gt;
      &lt;svg class=&quot;h-5 w-5 text-blue-400&quot; viewBox=&quot;0 0 20 20&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&gt;
        &lt;path fill-rule=&quot;evenodd&quot; d=&quot;M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z&quot; clip-rule=&quot;evenodd&quot; /&gt;
      &lt;/svg&gt;
    &lt;/div&gt;
    &lt;div class=&quot;ml-3 flex-1 md:flex md:justify-between&quot;&gt;
      &lt;p class=&quot;text-sm text-blue-700 not-prose&quot;&gt;
        &lt;strong&gt;Fact&lt;/strong&gt;: Ruby&apos;s built-in Array and String are &lt;em&gt;monoids&lt;/em&gt; too. We never think about it — that&apos;s how we should treat monads too.
      &lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-monads/1.3/maybe/&quot;&gt;Maybe&lt;/a&gt; is an abstraction that allows us to express &lt;em&gt;absence of data&lt;/em&gt;. In practice, it enables us to do nil-safe computations and never worry about getting &lt;code class=&quot;highlighter-rouge&quot;&gt;undefined method for nil:NilClass&lt;/code&gt;. It acts like an &lt;em&gt;extremely explicit&lt;/em&gt; alternative to &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;. It may be good when you return it from a &lt;a href=&quot;https://medium.com/@laertis.pappas/repository-pattern-in-ruby-i-decoupling-activerecord-and-persistence-e395e1b0cf69&quot;&gt;repository&lt;/a&gt;, but I don’t use it my models.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-monads/1.3/try/&quot;&gt;Try&lt;/a&gt; is a nice wrapper for exceptions. It’s helpful if you need to chain some actions, which may raise an exception. The standard exception-catching mechanism may break the flow and make you jump around the code to get the full picture. Try saves you from this.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-monads/1.3/task/&quot;&gt;Task&lt;/a&gt; is a wrapper around &lt;a href=&quot;https://github.com/ruby-concurrency/concurrent-ruby&quot;&gt;concurrent ruby’s&lt;/a&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;Promise&lt;/code&gt;. I’ve seen folks use it as an easy way to do concurrent IO – download things, write to database, etc. Just take at look at Vasily Kolesnikov’s &lt;a href=&quot;https://gist.github.com/v-kolesnikov/c5807aab0ac7ba5d1ba5e31be32e21e6&quot;&gt;asynchronous file downloader&lt;/a&gt;. It receives a list of URLs and creates tasks to download them. Without many low-level asynchronous details.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-monads/1.3/result/&quot;&gt;Result&lt;/a&gt; is the most common monad in a Ruby world. We use it to express result of a function call. It’s most useful when the result can be a &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt; or a &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;, which works as natural as it sounds. You can just &lt;code class=&quot;highlighter-rouge&quot;&gt;return Success(with_your_data)&lt;/code&gt; from your function and work with it. If something goes wrong, you would &lt;code class=&quot;highlighter-rouge&quot;&gt;return Failure(&quot;and provide some details&quot;)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are other monads, but I’ll omit them as those are not &lt;em&gt;that&lt;/em&gt; common and won’t benefit to the story.&lt;/p&gt;

&lt;h1 id=&quot;the-beef-with-monads&quot;&gt;The beef with monads&lt;/h1&gt;

&lt;p&gt;So, what’s the problem with monads? They seem to be a pretty controversial topic, but why? They seem to be pretty good at doing their job. What’s wrong with that?&lt;/p&gt;

&lt;p&gt;Everyone has their own problems with monads. Some folks get mildly annoyed by little things. Others are outraged that such an abomination exists in Ruby. Just see what folks have been telling me:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Monads are only useful in statically typed languages and only cause trouble in Ruby&lt;/li&gt;
  &lt;li&gt;Functional programming doesn’t look right in Ruby&lt;/li&gt;
  &lt;li&gt;Monads don’t feel like a right fit in Ruby&lt;/li&gt;
  &lt;li&gt;It’s just a syntactic sugar for if and else (&lt;em&gt;speaking about Result&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;We already have exceptions for that (&lt;em&gt;speaking about Result&lt;/em&gt;)&lt;/li&gt;
  &lt;li&gt;It’s over-engineering&lt;/li&gt;
  &lt;li&gt;People who use monads look like a cult&lt;/li&gt;
  &lt;li&gt;Don’t try to bring Haskell into Ruby&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s just a list of things off the top of my head. Sure, I’m paraphrasing, but the main idea looks like this.&lt;/p&gt;

&lt;p&gt;Some people who share those things are nice, friendly and welcoming; while some are hostile and won’t accept any reason. I won’t try to generalize based on their background or any other factor. Instead, I will address some misconceptions and speak about emotions that drive them.&lt;/p&gt;

&lt;h2 id=&quot;is-ruby-really-the-right-place-for-those-things&quot;&gt;Is Ruby really the right place for those things?&lt;/h2&gt;

&lt;p&gt;This issue is probably the most popular one. There’s actually a lot of reasons for this skepticism, let’s think about them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monads are a concept from the category theory.&lt;/strong&gt; Math. Their representation in software development is mostly &lt;code class=&quot;highlighter-rouge&quot;&gt;Haskell&lt;/code&gt;, which uses them &lt;em&gt;heavily&lt;/em&gt;, and it’s definitely not a mainstream language. It creates a subconscious association: haskell / monads =  something difficult and clumsy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They come from other languages.&lt;/strong&gt; The &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; type gets a lot of usage in Rust, Kotlin, Swift, F#, OCaml/ReasonML and Elm too. All of those languages are statically typed and compiled, which enables to do extra static analysis. For example, the compilers may check if you’ve handled all possible cases. Those checks are nearly impossible to implement in Ruby.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ruby is an object-oriented language.&lt;/strong&gt; Monads are a concept from functional programming, which may seem a bit odd and counter-intuitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You’ll have to learn a new interface.&lt;/strong&gt; When you take a look at dry-monads, you’ll see unfamiliar methods like &lt;code class=&quot;highlighter-rouge&quot;&gt;#bind&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;#fmap&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;#or_fmap&lt;/code&gt;. Those things require some additional learning and are not so trivial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There’s something we never did before.&lt;/strong&gt; The so-called &lt;a href=&quot;/2018/05/27/do-notation-ruby.html&quot;&gt;do notation&lt;/a&gt; is a syntactic sugar which looks unfamiliar to Ruby developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combining different kinds of monads may be troublesome.&lt;/strong&gt; As an example, you may treat &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; as a &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt;, which will definitely result in bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It requires extra effort to learn and adapt&lt;/strong&gt;. This is one of the most bitter points here. Having to learn may be annoying, and the whole process isn’t easy.&lt;/p&gt;

&lt;p&gt;Those things boil down to four points:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Are they really idiomatic?&lt;/li&gt;
  &lt;li&gt;Are they useful?&lt;/li&gt;
  &lt;li&gt;What trouble will they cause? Will they create bigger problems?&lt;/li&gt;
  &lt;li&gt;Is it difficult to learn?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ll speak about the usefulness and problems in &lt;a href=&quot;#my-own-perspective&quot;&gt;My own perspective&lt;/a&gt;, so let’s see if it’s idiomatic and/or difficult.&lt;/p&gt;

&lt;p&gt;Let me show you a couple of examples that demonstrate many ways to use the library. Labels are expandable — click on them to see the code.&lt;/p&gt;

&lt;details&gt;
  &lt;summary&gt;
1. &lt;strong&gt;Basic usage.&lt;/strong&gt; Trying to create a record and returning a value depending on the outcome.
&lt;/summary&gt;

  &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# https://github.com/saintprug/rubytalks.org/blob/cb32cff14587e021e71f0e5547765e84cd014c0d/lib/domains/talks/operations/create.rb#L43-L51&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_talk_speaker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speaker_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;talk_speaker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;talks_speakers_repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;talk_id: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;speaker_id: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;speaker_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;talk_speaker&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_speaker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;could not create talk_speaker&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;
2. &lt;strong&gt;Composing multiple operations&lt;/strong&gt;. A function that fetches or creates a record. It shows chaining functions using &lt;code class=&quot;highlighter-rouge&quot;&gt;#fmap&lt;/code&gt; — a method which works similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;Enumerable#map&lt;/code&gt;, but won&apos;t do anything if it&apos;s called on a &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;
&lt;/summary&gt;

  &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# https://github.com/davydovanton/cookie_box/blob/c7e92db9b69b38eb85fb9d7ef1f81706ea4830e6/lib/repositories/libs/get_or_create_repo.rb#L13-L22&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;repo_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;truncate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find_by_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;info_getter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fmap&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;
3. &lt;strong&gt;Composing multiple operations.&lt;/strong&gt; If one of them fails, the method will return a Failure and rollback the transaction. 
&lt;/summary&gt;

  &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# https://github.com/saintprug/rubytalks.org/blob/cb32cff14587e021e71f0e5547765e84cd014c0d/lib/domains/talks/operations/create.rb#L17-L28&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# rubocop:disable Metrics/AbcSize&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;talk_form&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;talk_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;symbolize_keys&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;oembed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generate_oembed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;talk_repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;transaction&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;speakers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find_or_create_speakers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:speakers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find_or_create_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;talk&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oembed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk_form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oembed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_talk_speakers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;speakers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;talk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;
4. &lt;strong&gt;Working with the computed result.&lt;/strong&gt; Using &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; with predicates to handle different cases
&lt;/summary&gt;

  &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# https://github.com/saintprug/rubytalks.org/blob/fe0a6f2c08f161e9bde9545227be6db5e1346539/lib/util/web/helpers/respond_with.rb#L9-L16&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;respond_with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;status: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;success?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;respond_with_success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;with: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;serializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;status: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetch_error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;respond_with_failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;status: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;

&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;
5. &lt;strong&gt;Working with the computed result.&lt;/strong&gt; Using &lt;code class=&quot;highlighter-rouge&quot;&gt;case&lt;/code&gt; to handle all cases
&lt;/summary&gt;

  &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# https://github.com/saintprug/retro-board/blob/b66a26a36cfc5ccfe8263fe0af31b3610ce2a896/apps/web/controllers/boards/show.rb#L10-L19&lt;/span&gt;

&lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Monads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mixin&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_a?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;value!&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;halt&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;These aren&apos;t the boards you&apos;re looking for&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;
6. &lt;strong&gt;Working with the computed result.&lt;/strong&gt; Using &lt;code class=&quot;highlighter-rouge&quot;&gt;#fmap&lt;/code&gt; to access the wrapped data
&lt;/summary&gt;

  &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# https://github.com/davydovanton/cookie_box/blob/c7e92db9b69b38eb85fb9d7ef1f81706ea4830e6/apps/web/controllers/decks/show.rb#L10-L17&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fmap&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@deck&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@issues&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:issues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Not found&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abilities&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;deck.read&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_account&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@deck&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;details&gt;
  &lt;summary&gt;
7. &lt;strong&gt;Working with the computed result.&lt;/strong&gt; Using pattern matching.
&lt;/summary&gt;

  &lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# a modification of example five, taken from saintprug/retro-board&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# it shows a more &quot;modern&quot; syntax for include&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# https://github.com/saintprug/retro-board/blob/b66a26a36cfc5ccfe8263fe0af31b3610ce2a896/apps/web/controllers/boards/show.rb#L10-L19&lt;/span&gt;

&lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Monads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;halt&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;These aren&apos;t the boards you&apos;re looking for&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;  &lt;/div&gt;
&lt;/details&gt;

&lt;p&gt;Here’s a couple of things to notice about those code styles:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They use blocks more often.&lt;/strong&gt; The interfaces use blocks or allow them. Longer chains become a norm, which goes against &lt;a href=&quot;https://rubocop.readthedocs.io/en/latest/cops_style/#stylemultilineblockchain&quot;&gt;current Rubocop defaults&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There’s no need for unnecessary naming.&lt;/strong&gt; Since there’s an option to easily chain transformations using &lt;code class=&quot;highlighter-rouge&quot;&gt;#fmap&lt;/code&gt; and other methods, we can avoid coming up with names we &lt;em&gt;don’t really need&lt;/em&gt;. I often feel frustrated about having to come up with names for intermediate data..&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We don’t build objects using &lt;code class=&quot;highlighter-rouge&quot;&gt;#new&lt;/code&gt;.&lt;/strong&gt; Instead, we use constructors that look like &lt;code class=&quot;highlighter-rouge&quot;&gt;Kernel#Array&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Kernel#String&lt;/code&gt; and similar methods. It looks pretty much like the standard Ruby code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We cherry-pick abstractions we need.&lt;/strong&gt; This helps prevent clutter and communicate more clearly. It looks boilerplaty, so folks move the includes to base classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can cherry-pick using a single &lt;code class=&quot;highlighter-rouge&quot;&gt;include&lt;/code&gt;.&lt;/strong&gt; It’s not a new pattern, but an uncommon one. Instead of using &lt;code class=&quot;highlighter-rouge&quot;&gt;include&lt;/code&gt; multiple times to get each abstraction, we list whatever we need: &lt;code class=&quot;highlighter-rouge&quot;&gt;[:result, :maybe, :try]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conditional logic uses predicates.&lt;/strong&gt; Nobody really reinvents the wheel, so if you need to add conditional logic — you’ve still got conditions and methods to check &lt;em&gt;which&lt;/em&gt; value you’ve got.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There’s an extensive support for case and pattern matching.&lt;/strong&gt; It enables us to avoid using many built-in interfaces altogether and write expressive and beautiful code. There aren’t too many examples, as the features are relatively new. If you’ve got something to share, please do!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The library introduces new semantics to &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt;.&lt;/strong&gt; Conventionally, we use &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; whenever we want to call a block. That’s exactly what’s going on here — we call a block. However, we bring the new semantics. Now, &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; looks more like &lt;code class=&quot;highlighter-rouge&quot;&gt;await&lt;/code&gt; in many languages, or similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; in Python or JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The “functional” programming part is barely noticeable&lt;/strong&gt;. None of those examples show us any mathematics-riddled word-buzzing functional programming some people expected to. Sure, it has &lt;em&gt;some&lt;/em&gt; features: naming, using blocks, immutable expressions. That’s pretty common in Ruby.&lt;/p&gt;

&lt;p&gt;In the end, is it idiomatic? I think so. It may be &lt;em&gt;unfamiliar&lt;/em&gt; to many developers, but it’s idiomatic almost all the way through. Except for the &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; expression, perhaps. After all, idiomatic just means following the conventions of the language.&lt;/p&gt;

&lt;h2 id=&quot;what-about-exceptions&quot;&gt;What about exceptions?&lt;/h2&gt;

&lt;p&gt;We use &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; to express the logic which may fail or not. Let’s imagine a simple use-case: user tries to log in and fills in their username/password combination. Here’s how we would handle it:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We make sure that user has provided username and password. If not, we fail&lt;/li&gt;
  &lt;li&gt;We make sure the username exists in our system. If not, we fail and suggest them to register&lt;/li&gt;
  &lt;li&gt;We make sure the password is correct.
    &lt;ul&gt;
      &lt;li&gt;If not, we try to check if it is one of the user’s old passwords. If so, fail and tell them they’ve changed passwords a while ago.&lt;/li&gt;
      &lt;li&gt;If it’s just an unknown password, fail and tell them the password is incorrect.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Make sure the user is not blocked in our system. Fail and warn them about it if they are&lt;/li&gt;
  &lt;li&gt;Create new session&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a common, yet complex example. It consists of multiple steps and has six outcomes: five unsuccessful and one successful.&lt;/p&gt;

&lt;p&gt;A lot of programmers would design this process using exceptions. They’d create five exception classes and just &lt;code class=&quot;highlighter-rouge&quot;&gt;raise&lt;/code&gt; them on demand. The higher-level code would have to &lt;code class=&quot;highlighter-rouge&quot;&gt;rescue&lt;/code&gt; them and handle as necessary.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; is an alternative approach to this design. It suggests that you return a &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt; or a &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt; with whatever data you need. The higher-level code would have to handle it using whatever technique they want – usually it’s a combination of Result’s built in methods and some &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;case&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;case in&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;Which way to choose is actually a matter of preference. How does your code make decisions? Do you need performance? Should we treat those errors like we treat out of memory and HTTP errors? Are errors data? What approach suits you better? We can’t answer all of those questions rationally. Trust me, I’ve tried.&lt;/p&gt;

&lt;p&gt;Sure, there are differences and similarities of the two techniques that might help us make a more informed decision. However, they deserve their own article. I’m working on it, so I’m going to just cut it short:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Exceptions are not an adequate replacement for Result, nor is Result an adequate replacement for exceptions. At least in Ruby&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those things serve different purpose:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Result is a way to express your &lt;em&gt;data&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Exceptions are a control flow mechanism&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You may replace one with another, but it &lt;em&gt;might&lt;/em&gt; be better for them to coexist.&lt;/p&gt;

&lt;p&gt;Remember I said about Rust, Kotlin, Swift, F#, OCaml/ReasonML, Haskell and Elm? Some of them have &lt;em&gt;both&lt;/em&gt; Result and exceptions. I encourage you to take a look at how those two ideas coexist in those languages and learn from it. Perhaps, it could bring your Ruby code to the next level.&lt;/p&gt;

&lt;h2 id=&quot;my-own-perspective&quot;&gt;My own perspective&lt;/h2&gt;

&lt;p&gt;I’m a fan of simple, yet efficient designs. Life is harder than that, so I have to deal with complex designs most of the time.&lt;/p&gt;

&lt;p&gt;When I’m dealing with something complex, I want to work with things that don’t confuse me. Yet, I’m ready to learn something new, especially if it’s something fundamental.&lt;/p&gt;

&lt;p&gt;I’m a domain-driven design enthusiast. I’m always looking for better ways to communicate my intentions via code as clearly as possible. Monads help me with this – they make my intentions clear.&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;#what-is-a-monad&quot;&gt;What is a monad&lt;/a&gt; I’ve listed four different monads and their purpose. In my business logic,  They help me answer the main question: “what’s going on here?”. I &lt;code class=&quot;highlighter-rouge&quot;&gt;return Success(...)&lt;/code&gt; when everything is good, I &lt;code class=&quot;highlighter-rouge&quot;&gt;Try&lt;/code&gt; to run some code which may fail with an error, I perform an asynchronous &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;, and I will &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; get a value when I query the database.&lt;/p&gt;

&lt;p&gt;Sure, I can pretend I’m writing Haskell and write code that’s hard to read even for someone who’s experienced with monads. It’s always up to me.&lt;/p&gt;

&lt;p&gt;I’ve been using monads since May/June 2016, and they’ve become as natural as &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Array&lt;/code&gt; and other built-in parts of the language. The learning curve was not that steep, but it took me over two years of mindless usage to somewhat &lt;em&gt;understand&lt;/em&gt; them. I was using them blindly – just returning &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; from functions and using whatever methods they provided. I want to emphasize that you can get at least &lt;em&gt;that&lt;/em&gt; long without needing to read about category theory.&lt;/p&gt;

&lt;p&gt;If you’re still skeptical about “having to learn” it, stay with me. There’s a website called “F# for fun and profit”, which is a source of many delightful resources on functional programming. Even though the &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; monad is a crucial part of many concepts described there, the word “monad” is actually &lt;a href=&quot;https://fsharpforfunandprofit.com/about/#banned&quot;&gt;banned from this site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There’s a problem with a lot of monad tutorials, including mine — they focus on maths instead of the practical application. This make us sound and act like we’re enlightened, like we’ve just had a revelation. We’re trying to speak about it because &lt;em&gt;now we get it&lt;/em&gt;. Regular folks don’t get it and think we’re some kind of a cult.&lt;/p&gt;

&lt;p&gt;In reality, monads are just simple building blocks. They’re a nice addition to your toolset, but you don’t need to &lt;em&gt;know&lt;/em&gt; that they are monads. It’s not important in the grand scheme of things — they only enable you to build greater things, like &lt;a href=&quot;https://fsharpforfunandprofit.com/rop/&quot;&gt;railway oriented programming&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another example of a better naming for monads is Python’s &lt;a href=&quot;https://returns.readthedocs.io/&quot;&gt;returns&lt;/a&gt;, which was heavily inspired by Ruby’s dry-monads. They don’t use the name monad anywhere. It’s just a library of &lt;em&gt;primitives&lt;/em&gt; to write &lt;em&gt;business logic&lt;/em&gt;. Nothing about endofunctors and category theory – that’s what I love about them.&lt;/p&gt;

&lt;p&gt;Monads sound scary and off-putting. They’re not that scary, and there’s nothing special about them. They just let us do greater things. Let’s move on and talk about that.&lt;/p&gt;

&lt;h1 id=&quot;how-do-i-persuade-my-team-to-use-monads&quot;&gt;How do I persuade my team to use monads&lt;/h1&gt;

&lt;p&gt;I had to answer it for the first time when &lt;a href=&quot;https://github.com/janko&quot;&gt;Janko&lt;/a&gt; approached me after EuRuKo 2018 to talk about it. I had no idea back then. It’s safe to say that I’ve learned something and can now speak about it.&lt;/p&gt;

&lt;p&gt;When people speak about introducing monads, they usually speak about using Result to organize domain logic. I’m going to focus on that.&lt;/p&gt;

&lt;p&gt;Here’s the thing:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Monads are not the goal. Think bigger&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you might have read in &lt;a href=&quot;#what-is-a-monad&quot;&gt;What is a monad&lt;/a&gt;, monads are just primitive tools that let you express something. Do you &lt;em&gt;really&lt;/em&gt; want to get stuck on such details? I thought I did.&lt;/p&gt;

&lt;p&gt;However, just using this piece of tech without seeing a &lt;em&gt;bigger picture&lt;/em&gt; will likely be fruitless or even frustrating. Please, don’t follow this path unless you have the authority to &lt;em&gt;just do it&lt;/em&gt;. You wouldn’t have needed this article in this case though, would you?&lt;/p&gt;

&lt;p&gt;Let’s try and see the bigger picture here. What exactly are you trying to solve?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Are you stuck with plain old fat models / fat controllers?&lt;/li&gt;
  &lt;li&gt;Are you struggling with exceptions in domain logic?&lt;/li&gt;
  &lt;li&gt;Do you need better performance than exceptions give you?&lt;/li&gt;
  &lt;li&gt;Do you want to introduce &lt;a href=&quot;https://fsharpforfunandprofit.com/rop/&quot;&gt;railway-oriented programming&lt;/a&gt;?&lt;/li&gt;
  &lt;li&gt;Are you frustrated with your current interactor / use case / service object implementation?&lt;/li&gt;
  &lt;li&gt;Do you just want to bring something new to your project to make it more appealing?&lt;/li&gt;
  &lt;li&gt;Do you want your development team to get out of their comfort zone and persuade them to learn something new?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you’ve answered those questions &lt;em&gt;honestly&lt;/em&gt;, we can move on to the next question.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What larger problems does it solve?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Please take your time to carefully assess the problems your project and business is facing. Talk to your peers, people from other departments and upper management. Talk about their problems, what they’d love to see and &lt;em&gt;maybe&lt;/em&gt; you’ll find out that you’ve got the solution. Perhaps not a solution but a part of it.&lt;/p&gt;

&lt;div class=&quot;rounded-md bg-blue-50 p-4&quot;&gt;
  &lt;div class=&quot;flex&quot;&gt;
    &lt;div class=&quot;flex-shrink-0&quot;&gt;
      &lt;svg class=&quot;h-5 w-5 text-blue-400&quot; viewBox=&quot;0 0 20 20&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&gt;
        &lt;path fill-rule=&quot;evenodd&quot; d=&quot;M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z&quot; clip-rule=&quot;evenodd&quot; /&gt;
      &lt;/svg&gt;
    &lt;/div&gt;
    &lt;div class=&quot;ml-3 flex-1 md:flex md:justify-between&quot;&gt;
      &lt;p class=&quot;text-sm text-blue-700 not-prose&quot;&gt;
Lots of things are easier when you&apos;ve got allies
      &lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Once you’ve done those steps, you should have enough evidence that your project needs some change. It may seem like monads are the only solution. Trust me, they’re not.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What are the alternatives? How good would they fit your situation?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Perhaps, you’d be better off with things like &lt;a href=&quot;https://github.com/collectiveidea/interactor/&quot;&gt;Interactor&lt;/a&gt;, &lt;a href=&quot;https://github.com/aaronmallen/activeinteractor&quot;&gt;ActiveInteractor&lt;/a&gt;. They let you implement the same things you could with Result, but in a little different fashion.&lt;/p&gt;

&lt;div class=&quot;rounded-md bg-blue-50 p-4&quot;&gt;
  &lt;div class=&quot;flex&quot;&gt;
    &lt;div class=&quot;flex-shrink-0&quot;&gt;
      &lt;svg class=&quot;h-5 w-5 text-blue-400&quot; viewBox=&quot;0 0 20 20&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&gt;
        &lt;path fill-rule=&quot;evenodd&quot; d=&quot;M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z&quot; clip-rule=&quot;evenodd&quot; /&gt;
      &lt;/svg&gt;
    &lt;/div&gt;
    &lt;div class=&quot;ml-3 flex-1 md:flex md:justify-between&quot;&gt;
      &lt;p class=&quot;text-sm text-blue-700 not-prose&quot;&gt;
If you&apos;re already using Interactor or ActiveInteractor, you might just patch the Context class to make it behave like a Result
      &lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;If you’re thinking about something greater, you might consider other kinds of architecture or even something like &lt;a href=&quot;https://eventide-project.org/&quot;&gt;Eventide&lt;/a&gt;. In this case, you’ll probably have to plan out the great refactoring, plan it out and try and push the idea. I’m in no position to give advice here, as it’s a pretty complex task and I can’t generalize it.&lt;/p&gt;

&lt;p&gt;If you’re still looking for a way to organize domain logic, you’ll have to learn and teach. Best way to learn is to practise: build a couple of tiny playgrounds. Try using &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; to organize the domain logic. Speak about it on &lt;a href=&quot;https://dry-rb.zulipchat.com&quot;&gt;dry-rb chat&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once you’ve learned enough to hold a conversation, gradually introduce your team to the new approaches. It works well if you have a designated space for sharing knowledge – talks, articles, tutorials, etc. If you don’t, you might as well just create one. It’s a nice improvement to your &lt;em&gt;engineering culture&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;the-answer&quot;&gt;The answer&lt;/h1&gt;

&lt;p&gt;I’ve covered a lot of ideas in a way that looks like a rant, but haven’t gotten to the main point: “should I &lt;em&gt;really&lt;/em&gt; use monads?”. Here’s the answer:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;It depends&lt;/strong&gt;. &lt;br /&gt;
Will they help you do greater things? Then the answer is yes. &lt;br /&gt;
Do they seem off-putting and less enjoyable than other things? The answer would be “no”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s okay if you haven’t understood the new tool yet. It’s also okay if you didn’t like it.&lt;/p&gt;

&lt;p&gt;Here’s the thing: the question is actually more of a &lt;em&gt;stylistic&lt;/em&gt; preference than something substantial. Decision to use a monad or not is a lot like the decision to use &lt;code class=&quot;highlighter-rouge&quot;&gt;if&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;case&lt;/code&gt;, or &lt;code class=&quot;highlighter-rouge&quot;&gt;collect&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;reduce&lt;/code&gt;. Just like pattern matching lets you express yourself, each monad contributes to expressiveness to your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bottom line:&lt;/strong&gt; monads are alright. They’re just a low-level detail that helps you build something greater. Yet, there are other solutions. Focus on the greater things and use whatever suits you better. Meanwhile, be open-minded about the alternatives. They’re alright too.&lt;/p&gt;

&lt;h1 id=&quot;links-and-references&quot;&gt;Links and references&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-monads/&quot;&gt;dry-monads&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dry-rb.zulipchat.com&quot;&gt;dry-rb chat&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://eventide-project.org/&quot;&gt;Eventide&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fsharpforfunandprofit.com/&quot;&gt;F# for fun and profit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fsharpforfunandprofit.com/rop/&quot;&gt;Railway oriented programming&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://gist.github.com/v-kolesnikov/c5807aab0ac7ba5d1ba5e31be32e21e6&quot;&gt;Vasily Kolesnikov’s asynchronous file downloader&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/aaronmallen/activeinteractor&quot;&gt;ActiveInteractor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/collectiveidea/interactor/&quot;&gt;Interactor&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/ruby-concurrency/concurrent-ruby&quot;&gt;Concurrent ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://returns.readthedocs.io/&quot;&gt;returns: monads in Python&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://nywkap.com/programming/either-monads-ruby.html&quot;&gt;Vitaly Pushkar’s article on Error handling with Monads in Ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/05/27/do-notation-ruby.html&quot;&gt;My own article on Do notation and railway oriented programming&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/09/08/monad-laws-in-ruby.html&quot;&gt;My own article on monad laws&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/saintprug/rubytalks.org&quot;&gt;rubytalks.org repo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/davydovanton/cookie_box/&quot;&gt;Anton Davydov’s cookie box repo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/saintprug/retro-board/&quot;&gt;Saint P Ruby’s retro board&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://morozov.is/images/previews/should-i-really-use-monads.png" /><media:content medium="image" url="https://morozov.is/images/previews/should-i-really-use-monads.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">dry-rb 1.0: upgrading validations, types and schemas</title><link href="https://morozov.is/2019/05/31/upgrading-dry-gems" rel="alternate" type="text/html" title="dry-rb 1.0: upgrading validations, types and schemas" /><published>2019-05-31T06:48:00+00:00</published><updated>2019-05-31T06:48:00+00:00</updated><id>repo://posts.collection/_posts/2019-05-31-upgrading-dry-gems.md</id><content type="html" xml:base="https://morozov.is/2019/05/31/upgrading-dry-gems">&lt;p&gt;I’m enthusiastic about &lt;a href=&quot;https://dry-rb.org/&quot;&gt;dry-rb gems&lt;/a&gt;. Actually, I’ve never worked on Ruby projects without a dry-rb gem. However, some people are sceptical, as a lot of core dry-rb gems are still in their &lt;code class=&quot;highlighter-rouge&quot;&gt;0.x&lt;/code&gt; phase, which leads to a lot of breaking changes and hours of refactoring.&lt;/p&gt;

&lt;p&gt;I’m happy to see dry-rb mature: dry-monads entered 1.0 phase in Summer 2018, and now &lt;a href=&quot;https://dry-rb.org/news/2019/04/23/dry-types-and-dry-struct-1-0-0-released/&quot;&gt;two more libraries&lt;/a&gt; hit v1.0 milestones: dry-types and dry-struct; and dry-validation is in its 1.0 RC phase.&lt;/p&gt;

&lt;p&gt;I haven’t updated my dry-rb gems for a couple of months, so I’ve missed a lot of breaking changes. Finally, I decided to upgrade the gems and write about the process. I’ll take a swing at &lt;em&gt;automating&lt;/em&gt; my upgrade process as much as I can.&lt;/p&gt;

&lt;!-- excerpt --&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Here’s what my dry-rb gems look like:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ bundle list | grep dry
  * dry-auto_inject (0.4.6)
  * dry-configurable (0.7.0)
  * dry-container (0.6.0)
  * dry-core (0.4.7)
  * dry-equalizer (0.2.1)
  * dry-events (0.1.0)
  * dry-inflector (0.1.2)
  * dry-initializer (2.5.0)
  * dry-logic (0.4.2)
  * dry-matcher (0.7.0)
  * dry-monads (1.2.0)
  * dry-struct (0.6.0)
  * dry-transaction (0.13.0)
  * dry-types (0.13.2)
  * dry-validation (0.12.1)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve got 15 gems, but I only care about four of them: monads, types, struct and validation. Since monads are up-to-date, I’m only going to talk about types, struct and validation.&lt;/p&gt;

&lt;p&gt;In this post, I’ll try to give a step-by-step guide that will &lt;em&gt;simplify&lt;/em&gt; the upgrading process. It won’t give a 100% working solutions, but it will probably save you a couple of hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;. I use macOS with GNU sed (&lt;code class=&quot;highlighter-rouge&quot;&gt;gsed&lt;/code&gt;) instead of built-in &lt;code class=&quot;highlighter-rouge&quot;&gt;sed&lt;/code&gt; command. So if you want to follow my instructions, install it via &lt;code class=&quot;highlighter-rouge&quot;&gt;brew install gnu-sed&lt;/code&gt;. Since I’m using &lt;a href=&quot;https://fishshell.com/&quot;&gt;fish&lt;/a&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;bash&lt;/code&gt; / &lt;code class=&quot;highlighter-rouge&quot;&gt;zsh&lt;/code&gt;, some commands might need slight modifications to work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;. I wrote this article while upgrading the dry-rb gems on my project. I decided to do it gradually — so you might encounter some redundant steps. If you do, please contact me via email and I’ll upgrade it.&lt;/p&gt;

&lt;h2 id=&quot;dry-validation-to-dry-schema&quot;&gt;dry-validation to dry-schema&lt;/h2&gt;

&lt;p&gt;The gem we knew as &lt;code class=&quot;highlighter-rouge&quot;&gt;dry-validation&lt;/code&gt; has evolved from a complex schema validation &amp;amp; coercion into a high-level contract DSL with domain logic.&lt;/p&gt;

&lt;p&gt;Meanwhile, it has become so complex they decided to break it down into two gems: dry-validation and &lt;a href=&quot;https://solnic.codes/2019/01/31/introducing-dry-schema/&quot;&gt;dry-schema&lt;/a&gt;. The latter provides the old functionality of &lt;code class=&quot;highlighter-rouge&quot;&gt;dry-validation&lt;/code&gt; — the schema validations, coercions, and they fixed &lt;em&gt;all&lt;/em&gt; known issues. &lt;code class=&quot;highlighter-rouge&quot;&gt;dry-validation&lt;/code&gt; adds domain rules and validations on top of that.&lt;/p&gt;

&lt;p&gt;I don’t want to go around and update everything manually, so I’m going to replace &lt;code class=&quot;highlighter-rouge&quot;&gt;dry-validation&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;dry-schema&lt;/code&gt; as much as I can, and manually refactor the rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;. Upgrade dry-validation to &lt;code class=&quot;highlighter-rouge&quot;&gt;0.13&lt;/code&gt;. It’s the last version before the switch, so if your builds pass — you’re good to go. You’ll have to update dry-types to &lt;code class=&quot;highlighter-rouge&quot;&gt;0.14&lt;/code&gt; too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;. Replace dry-validation with equivalent dry-schema version (0.1.0) and replace all &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Validation&lt;/code&gt; occurrences with &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Schema&lt;/code&gt;. Also replace all &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Validation.Schema&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Validation.define&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ bundle remove dry-validation &amp;amp;&amp;amp; bundle add dry-schema --version 0.1.0`
$ grep -rl &apos;Dry::Validation&apos; ./**/*.rb | xargs gsed -i &apos;s/Dry::Validation/Dry::Schema/g&apos;
$ grep -rl &apos;Dry::Schema.Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/Dry::Schema.Schema/Dry::Schema.define/g&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you’ve used &lt;a href=&quot;https://dry-rb.org/gems/dry-validation/extensions/struct/&quot;&gt;struct extension&lt;/a&gt;, don’t forget to search for &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Schema.load_extensions&lt;/code&gt; and remove &lt;code class=&quot;highlighter-rouge&quot;&gt;:struct&lt;/code&gt; from the list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3&lt;/strong&gt;. Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;.each(&amp;amp;:type?)&lt;/code&gt; predicates with &lt;code class=&quot;highlighter-rouge&quot;&gt;.each(:type?)&lt;/code&gt;. The same goes for &lt;code class=&quot;highlighter-rouge&quot;&gt;maybe&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;filled&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;value&lt;/code&gt;. You might get &lt;code class=&quot;highlighter-rouge&quot;&gt;ArgumentError: no receiver given&lt;/code&gt; if you don’t.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/\.\(filled\|value\|each\|maybe\)(&amp;amp;/.\1(/g&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 4&lt;/strong&gt;. Refactor schemas that use &lt;a href=&quot;https://dry-rb.org/gems/dry-validation/0.13/array-as-input/&quot;&gt;arrays as input&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The feature has been removed and it’s not coming back until dry-schema 1.0. Here’s &lt;a href=&quot;https://github.com/dry-rb/dry-schema/issues/22&quot;&gt;an issue&lt;/a&gt; with the feature.&lt;/p&gt;

&lt;p&gt;The refactoring will look like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Before&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ItemSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Params&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:int?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;option_ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:int?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ItemSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;ItemSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Params&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:item_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:int?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:option_ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:int?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


&lt;span class=&quot;no&quot;&gt;ItemSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;input: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;. Check you’ve ever inherited from &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Validation&lt;/code&gt; schemas. If you did, do the following transformations:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Rename classes&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Validation::Schema::Params&lt;/code&gt; → &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Schema::Params&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Validation::Schema::JSON&lt;/code&gt; → &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Schema::JSON&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Validation::Schema&lt;/code&gt; → &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Schema&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
  &lt;li&gt;Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;define!&lt;/code&gt; block with &lt;code class=&quot;highlighter-rouge&quot;&gt;define&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Move &lt;code class=&quot;highlighter-rouge&quot;&gt;configure&lt;/code&gt; block under &lt;code class=&quot;highlighter-rouge&quot;&gt;define&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;
&lt;span class=&quot;c1&quot;&gt;# Before:&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Validation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Params&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:i18n&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After:&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ApplicationSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Params&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:i18n&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And update its subclasses:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Before&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MySchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationSchema&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:yaml&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;define!&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# your params go here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MySchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationSchema&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;messages&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:yaml&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# your params go here&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;. Update DSL inheritance.&lt;/p&gt;

&lt;p&gt;Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Schema.Params(BaseClass)&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Schema.Params(parent: BaseClass)&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/Dry::Schema\(\(::\)\|\.\)\(Params\|JSON\|Schema\)(\([[:alnum:]]*\))/Dry::Schema\1\3(parent: \4)/g&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Before you proceed&lt;/strong&gt; Skip steps 7 and 8 if you’ve never used &lt;a href=&quot;https://dry-rb.org/gems/dry-validation/0.13/type-specs/&quot;&gt;type specs API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 7&lt;/strong&gt;. Remove &lt;code class=&quot;highlighter-rouge&quot;&gt;config.type_specs&lt;/code&gt; from your schemas&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;config.type_specs&apos; ./**/*.rb | xargs gsed -i &apos;/config\.type_specs/d&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 8&lt;/strong&gt;. Remove type spec usages from &lt;code class=&quot;highlighter-rouge&quot;&gt;required&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;optional&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/\(required\|optional\)(\(:[[:alnum:]_]*\), [[:print:]]*)\(\.\|$\)/\1(\2)\3/g&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;updating-dry-schema-to-03&quot;&gt;Updating dry-schema to 0.3&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 9&lt;/strong&gt;. Update your gemfile to specify &lt;code class=&quot;highlighter-rouge&quot;&gt;gem &apos;dry-schema&apos;, &apos;~&amp;gt; 0.3.0&apos;&lt;/code&gt; and run &lt;code class=&quot;highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 10&lt;/strong&gt;. If you’re using I18n, move &lt;code class=&quot;highlighter-rouge&quot;&gt;errors&lt;/code&gt; under &lt;code class=&quot;highlighter-rouge&quot;&gt;dry_schema&lt;/code&gt; namespace. This way,&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;array?&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;must be an array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;will turn into&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;en&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;dry_schema&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;array?&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;must be an array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 11&lt;/strong&gt;. Find any &lt;code class=&quot;highlighter-rouge&quot;&gt;schema&lt;/code&gt; macro usages and replace them with &lt;code class=&quot;highlighter-rouge&quot;&gt;hash&lt;/code&gt;, as &lt;code class=&quot;highlighter-rouge&quot;&gt;schema&lt;/code&gt; no longer prepends &lt;code class=&quot;highlighter-rouge&quot;&gt;value(:hash?)&lt;/code&gt; check.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Before&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/schema \(do\|{\)/hash \1/g&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 12&lt;/strong&gt;. Find any &lt;code class=&quot;highlighter-rouge&quot;&gt;each&lt;/code&gt; macro usages and replace them with &lt;code class=&quot;highlighter-rouge&quot;&gt;array&lt;/code&gt; to add type check. Since Ruby has a &lt;code class=&quot;highlighter-rouge&quot;&gt;Enumerable#each&lt;/code&gt; function, we can’t automate it, but we can still find possible occurrences:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;each \(do\|{\)&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Feel free to skip if you feel like you don’t need type checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 13&lt;/strong&gt;. Load &lt;a href=&quot;https://dry-rb.org/gems/dry-schema/extensions/hints/&quot;&gt;hints extension&lt;/a&gt; if you use monads or &lt;code class=&quot;highlighter-rouge&quot;&gt;.messages&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;load_extensions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:hints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-leap-towards-100&quot;&gt;The leap towards 1.0.0&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 14&lt;/strong&gt;. Update dry-struct, dry-types and dry-schema and run &lt;code class=&quot;highlighter-rouge&quot;&gt;bundle install&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dry-schema&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;~&amp;gt; 1.1.0&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dry-struct&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;~&amp;gt; 1.0.0&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dry-types&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;~&amp;gt; 1.0.0&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 15&lt;/strong&gt;. Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Types.module&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry.Types(default: :nominal)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you’ve never used nominal types (i.e. &lt;code class=&quot;highlighter-rouge&quot;&gt;Types::Hash&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Types::Integer&lt;/code&gt;), feel free to use &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry.Types&lt;/code&gt; instead.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ gsed -i &apos;s/Dry::Types\.module/Dry.Types(default: :nominal)/g&apos; ./**/*.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 16&lt;/strong&gt;. Replace legacy hash schemas with new ones. See https://dry-rb.org/gems/dry-types/0.15/hash-schemas/&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 17&lt;/strong&gt;. Update error message config&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;config.messages&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;config.messages.backend&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;config.messages_file = &apos;/path/to/my/errors.yml&apos;&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;config.messages.load_paths &amp;lt;&amp;lt; &apos;/path/to/my/errors.yml&apos;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;config.namespace = :user&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;config.messages.namespace = :user&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/config\.messages =/config.messages.backend =/g&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/config\.messages_file =/config.messages.load_paths &amp;lt;&amp;lt;/g&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/config\.namespace =/config.messages.namespace =/g&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 18&lt;/strong&gt;. Symbolize all string keys&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/\(required\|optional\)(\(\&apos;\|&quot;\)\([[:alnum:]_]*\)\(\&apos;\|&quot;\)/\1(:\3/g&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 19&lt;/strong&gt;. Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;Types.Definition&lt;/code&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;Types.Nominal&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ gsed -i &apos;s/Types\.Definition/Types.Nominal/&apos; ./**/*.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 20&lt;/strong&gt;. If you rely on &lt;code class=&quot;highlighter-rouge&quot;&gt;Types::Params&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Types::JSON&lt;/code&gt; not to raise an exception on invalid input, decorate the definitions with &lt;code class=&quot;highlighter-rouge&quot;&gt;.lax&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ gsed -i &apos;s/Types::JSON::\([[:alnum:]]*\)/Types::JSON::\1.lax/g&apos; ./**/*.rb
$ gsed -i &apos;s/Types::Params::\([[:alnum:]]*\)/Types::Params::\1.lax/g&apos; ./**/*.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 21&lt;/strong&gt;. Replace &lt;code class=&quot;highlighter-rouge&quot;&gt;:type?&lt;/code&gt; predicates with type checks wherever you need this&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/\(filled\|maybe\|value\)(:str?/\1(:string/g&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/\(filled\|maybe\|value\)(:int?/\1(:integer/g&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs gsed -i &apos;s/\(filled\|maybe\|value\)(:date?/\1(:date/g&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 22&lt;/strong&gt;. &lt;code class=&quot;highlighter-rouge&quot;&gt;Result#{messages, errors, hints}&lt;/code&gt; now return &lt;code class=&quot;highlighter-rouge&quot;&gt;MessageSet&lt;/code&gt;, which can be converted to &lt;code class=&quot;highlighter-rouge&quot;&gt;Hash&lt;/code&gt;. So we need to go and update the usages &lt;em&gt;everywhere&lt;/em&gt;. Also &lt;code class=&quot;highlighter-rouge&quot;&gt;Result#to_monad&lt;/code&gt; now wraps entire &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; object, so we have to update our code.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Before&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;errors: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;errors&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;errors: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_monad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;failure&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;errors: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;errors: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_monad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve used the scripts to help me look and trace those values:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;\.messages&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;\.errors&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;\.to_monad&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;\.failure&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;\.value_or&apos;
$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;\.value!&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;refactoring-to-dry-validation&quot;&gt;Refactoring to dry-validation&lt;/h2&gt;

&lt;p&gt;The steps above should be good enough to update most of the features, but if you ‘ve ever used &lt;a href=&quot;https://dry-rb.org/gems/dry-validation/0.13/high-level-rules/&quot;&gt;high-level rules&lt;/a&gt;, &lt;a href=&quot;https://dry-rb.org/gems/dry-validation/0.13/custom-validation-blocks/&quot;&gt;validation blocks&lt;/a&gt;, you have two options: either remove those features from your schemas, or use dry-validation 1.0. I decided to refactor most of my schemas, that’s what came out of it.&lt;/p&gt;

&lt;p&gt;There are things to keep in mind during the update:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dry-validation&lt;/code&gt; is a library to validate &lt;em&gt;domain&lt;/em&gt; logic and rules. The core concept is a &lt;code class=&quot;highlighter-rouge&quot;&gt;Contract&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;All contracts must be instantiated — no more &lt;code class=&quot;highlighter-rouge&quot;&gt;Schema.call&lt;/code&gt;. We need to use &lt;code class=&quot;highlighter-rouge&quot;&gt;Contract.new.call&lt;/code&gt; now&lt;/li&gt;
  &lt;li&gt;The idiomatic way to define a contract is to use standard Ruby syntax: &lt;code class=&quot;highlighter-rouge&quot;&gt;class Contract &amp;lt; Dry::Validation::Contract&lt;/code&gt; as opposed to dry-schema’s &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Schema.Params { }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 23&lt;/strong&gt;. Update dependency injection. The new version uses &lt;a href=&quot;http://dry-rb.org/gems/dry-initializer/&quot;&gt;dry-initializer&lt;/a&gt; under the hood, so it works like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;use &lt;code class=&quot;highlighter-rouge&quot;&gt;option&lt;/code&gt; for keyword arguments&lt;/li&gt;
  &lt;li&gt;use &lt;code class=&quot;highlighter-rouge&quot;&gt;param&lt;/code&gt; for positional arguments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll have to pass the arguments when you instantiate the method&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Before&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Validation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:repo&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;repo: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;my_repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Contract&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Validation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Contract&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;option&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:repo&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;contract&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;repo: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;contract&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used the script to find the files I need to refactor:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;option :[[:alnum:]_]*$&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 24&lt;/strong&gt;. Rewrite rules and validations. I can’t provide a comprehensive migration guide because I’ve just refactored everything and tried to make my specs pass without giving it much thought.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# Before&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Validation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Params&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;type_specs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;define!&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;format?: &lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\A\d{13,19}\z/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;format?: &lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\A(0?[1-9]|1[012])\z/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;expired: &lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;%i[year month]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;20&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;end_of_month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;current&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# After&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreditCardSchema&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Validation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Contract&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;format?: &lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\A(0?[1-9]|1[012])\z/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;filled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;format?: &lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/\A\d{2}\z/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;rule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;year&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;20&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;month&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;end_of_month&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:expired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:expired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# ^ a little duplication here to produce the expected error message&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# without refactoring anything else&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I used this script to search for all schemas that need rewriting:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;$ grep -rl &apos;Schema&apos; ./**/*.rb | xargs grep -n &apos;rule\|validate(&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Step 25&lt;/strong&gt; (optional). If you’re using Reform, you’re in for a disappointment, especially if you’ve been using its &lt;code class=&quot;highlighter-rouge&quot;&gt;dry-validation&lt;/code&gt; DSL.&lt;/p&gt;

&lt;p&gt;We have Reform 2.2.4 with ActiveModel validations, so we &lt;a href=&quot;https://github.com/Qlean/reform/&quot;&gt;forked it&lt;/a&gt; and removed all the dry-validation stuff. Feel free to fork and use!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 26&lt;/strong&gt;. Fix the rest of failing specs. All done!&lt;/p&gt;

&lt;h2 id=&quot;recap&quot;&gt;Recap&lt;/h2&gt;

&lt;p&gt;The upgrade process took me about 3 work days of refactoring, and I was glad I learned basic &lt;code class=&quot;highlighter-rouge&quot;&gt;sed&lt;/code&gt; to help me — it’s annoying to do so much manual work.&lt;/p&gt;

&lt;p&gt;However, I think the improvements are worth it. The ones I like the most:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dry-types&lt;/code&gt; is stricter and less verbose now — if you’re not including nominal types, then &lt;code class=&quot;highlighter-rouge&quot;&gt;Types::String&lt;/code&gt; is the same as &lt;code class=&quot;highlighter-rouge&quot;&gt;Types::Strict::String&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The known dry-validation bugs were fixed&lt;/li&gt;
  &lt;li&gt;Decreased complexity of schema validations&lt;/li&gt;
  &lt;li&gt;New library to design domain validations and contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I urge you to try the new dry-rb gems — and write about your experience. If you’ve upgraded your gems and wrote a post about your journey and update process — please send me an email and I’ll add a link to your page. And of course, it would be great to see new contributions to &lt;a href=&quot;https://github.com/dry-rb/dry-rb.org&quot;&gt;official docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://solnic.codes/2019/01/31/introducing-dry-schema/&quot;&gt;Introducing dry-schema&lt;/a&gt; @ solnic.codes&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://discourse.dry-rb.org/t/plans-for-dry-validation-dry-schema-a-new-gem/215&quot;&gt;How it all started&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-schema/&quot;&gt;dry-schema&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-validation/&quot;&gt;dry-validation&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-types/1.0/&quot;&gt;dry-types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Update (01.06.2019)&lt;/strong&gt;. &lt;a href=&quot;https://github.com/flash-gordon&quot;&gt;flash-gordon&lt;/a&gt; pointed out that you don’t need to wrap config into the &lt;code class=&quot;highlighter-rouge&quot;&gt;configure&lt;/code&gt; block. So I’ve replaced&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xxx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yyy&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;with a less nested version:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;
&lt;span class=&quot;n&quot;&gt;define&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;xxx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yyy&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Update (07.06.2019)&lt;/strong&gt;. &lt;a href=&quot;https://github.com/solnic/&quot;&gt;solnic&lt;/a&gt; pointed out that I made a typo in &lt;strong&gt;Step 10&lt;/strong&gt;: it used to say &lt;code class=&quot;highlighter-rouge&quot;&gt;dry_struct&lt;/code&gt; instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;dry_schema&lt;/code&gt;. I’ve updated the step accordingly.&lt;/p&gt;</content><author><name></name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://morozov.is/images/previews/upgrading_dry_gems.png" /><media:content medium="image" url="https://morozov.is/images/previews/upgrading_dry_gems.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Partial application in Ruby</title><link href="https://morozov.is/2019/01/11/partial-application-in-ruby" rel="alternate" type="text/html" title="Partial application in Ruby" /><published>2019-01-11T21:00:15+00:00</published><updated>2019-01-11T21:00:15+00:00</updated><id>repo://posts.collection/_posts/2019-01-12-partial-application-in-ruby.md</id><content type="html" xml:base="https://morozov.is/2019/01/11/partial-application-in-ruby">&lt;p&gt;Ruby is a multi-paradigm language with a strong bias towards object-oriented programming. You can argue that its design is influenced by Alan Kay and Smalltalk, as opposed to C++/Java-style object-oriented languages. Thankfully, this object-oriented design doesn’t mean we can’t use ideas from functional programming. There’s a small list of functional traits in Ruby:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Expression-oriented syntax&lt;/li&gt;
  &lt;li&gt;Geeky names for &lt;code class=&quot;highlighter-rouge&quot;&gt;Enumerable&lt;/code&gt; methods: &lt;code class=&quot;highlighter-rouge&quot;&gt;filter&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;map&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;reduce&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;flat_map&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Idiomatic &lt;a href=&quot;https://dry-rb.org/gems/dry-monads&quot;&gt;monads&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2018/05/27/do-notation-ruby.html&quot;&gt;Railway oriented programming&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;lambdas and procs&lt;/li&gt;
  &lt;li&gt;… I can go on and on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s also one specific empowering feature: built-in support for partial application. In this article, I want to talk about implementation and use-cases for partial application in Ruby.&lt;/p&gt;

&lt;!-- excerpt --&gt;

&lt;h1 id=&quot;proxy-methods&quot;&gt;Proxy methods&lt;/h1&gt;

&lt;p&gt;Let’s say we have a function of two arguments. As an example, we’ll take a function that accepts two strings: &lt;em&gt;tag&lt;/em&gt; and &lt;em&gt;text&lt;/em&gt; and formats them to look like &lt;strong&gt;[tag] text&lt;/strong&gt;. The source code for this function would be fairly simple:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tagged_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;[&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;tagged_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFO&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Hello, World!&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; &apos;[INFO] Hello, World!&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s say we build a logger that only uses 3 tags: &lt;em&gt;INFO&lt;/em&gt;, &lt;em&gt;WARNING&lt;/em&gt;, and &lt;em&gt;ERROR&lt;/em&gt;; and implements only one method, which logs to stdout.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Log&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tagged_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, to use it, we have to always call &lt;code class=&quot;highlighter-rouge&quot;&gt;Log.new.write(&apos;INFO&apos;, &apos;log data&apos;)&lt;/code&gt;, which is not convenient. Besides, what happens if we add different log levels? What if we make a typo, or write &lt;em&gt;DEbUG&lt;/em&gt; instead of &lt;em&gt;DEBUG&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Let’s improve the code and write a couple of methods that will help us avoid any mistakes with the tags.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Log&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;INFO&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;warning&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;WARNING&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;ERROR&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, instead of a single method with two arguments, we have three methods that accept one argument. Those methods are just &lt;em&gt;proxies&lt;/em&gt; to the original method as they don’t have any extra logic; they just &lt;strong&gt;fix the first argument&lt;/strong&gt; of the original method. This technique is called &lt;strong&gt;partial application&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Partial application is the process of fixing a number of arguments to a function, producing another function of smaller arity&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This Wikipedia definition explains what we’ve done here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;We have a function (&lt;em&gt;write&lt;/em&gt;) that accepts two arguments — which means, its arity is 2&lt;/li&gt;
  &lt;li&gt;We defined functions &lt;em&gt;error&lt;/em&gt;, &lt;em&gt;warning&lt;/em&gt;, and &lt;em&gt;info&lt;/em&gt; that accept only one argument, so their arity is 1&lt;/li&gt;
  &lt;li&gt;Those functions only pass their input to write &lt;em&gt;write&lt;/em&gt; — we &lt;strong&gt;fixed&lt;/strong&gt; the first argument and passed the rest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever we make a function that only calls another one, but requires fewer arguments, we can talk about partial application. In practice, we use it to reduce boilerplate, encapsulate logic, and make our lives easier. I bet you’ve used it countless times already, but probably never considered that this “pattern“ might have a name.&lt;/p&gt;

&lt;h1 id=&quot;without-methods&quot;&gt;Without methods&lt;/h1&gt;

&lt;p&gt;What if we don’t want to extend our class, but still want to use partial application? Our only solution is procs. Personally, I prefer to use lambdas for the task. Let’s see how they work.&lt;/p&gt;

&lt;p&gt;We have a &lt;em&gt;Log&lt;/em&gt; with a simple public interface: &lt;em&gt;write(tag, string)&lt;/em&gt;. I’m building a system which requires its own tag: &lt;em&gt;SECURITY&lt;/em&gt;, but I will only need to use it in one class.&lt;/p&gt;

&lt;p&gt;In this case, I would do something like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Instantiate the &lt;code class=&quot;highlighter-rouge&quot;&gt;Log&lt;/code&gt; and save the object into a variable&lt;/li&gt;
  &lt;li&gt;Define a lambda that uses the object and calls &lt;code class=&quot;highlighter-rouge&quot;&gt;#write&lt;/code&gt; on it&lt;/li&gt;
  &lt;li&gt;Use the lambda whenever I want to log something&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is how it looks in a class:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SecurityService&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:logger_instance&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logger_instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@logger_instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger_instance&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger_instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SECURITY&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Hello, World!&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;SecurityService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; will print &quot;[SECURITY] Hello, World!&quot;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We managed not to define any extra methods, but we still had to manually create a lambda. What if we could avoid it? Then the code would be a little bit simpler:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;vi&quot;&gt;@log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger_instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;SECURITY&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;Hello, World!&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Unfortunately, Ruby doesn’t work this way and we’ll just get an exception. However, it gives us a couple of tools to implement what we want.&lt;/p&gt;

&lt;h1 id=&quot;metaprogramming&quot;&gt;Metaprogramming&lt;/h1&gt;

&lt;p&gt;Let’s use Ruby’s metaprogramming to write a helper will enable us to pass fewer arguments to our methods. Here’s how it would work:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You pass a function to the helper&lt;/li&gt;
  &lt;li&gt;Helper returns a modified function&lt;/li&gt;
  &lt;li&gt;If we call the modified function and provide all arguments, it works as usual&lt;/li&gt;
  &lt;li&gt;If we provide fewer arguments than required, we get a new function that requires the rest of the arguments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how it would look like:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;enable_partial_application&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# our helper&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# our function&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enable_partial_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; new function&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 5&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 12&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sounds cool, right? Let’s see how we can implement this in Ruby. I’m going to pollute global namespace and define a method &lt;code class=&quot;highlighter-rouge&quot;&gt;enable_partial_application&lt;/code&gt; that accepts a function and returns a wrapper function.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enable_partial_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s start adding logic piece-by-piece. Here’s first piece of logic: “If we provide enough arguments, we call the original function”. To do so, we need to know exactly how many arguments the function requires — so we use the built-in method &lt;em&gt;#arity&lt;/em&gt;, which gives us the number.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enable_partial_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# I use `&amp;lt;=` instead of `==` because I want Ruby to&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# handle cases when there are too many arguments.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# ???&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alright, let’s handle the case when there are fewer arguments than required. We’ll return a new function that remembers our previous input:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enable_partial_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt;

  &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Okay, now we can test it:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enable_partial_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 5&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 12&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It works, alright. Let’s check out functions with more arguments:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;enable_partial_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 8&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 6&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; ArgumentError&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# (wrong number of arguments (given 2, expected 3))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last line fails because we need to make &lt;em&gt;enable_partial_application&lt;/em&gt; work recursively. We can fix this by updating two lines:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enable_partial_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;=&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;=&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now it works like a charm — the resulting function keeps calling itself until the user has provided enough arguments.&lt;/p&gt;

&lt;p&gt;One last thing though. It doesn’t work with functions that take a dynamic number of arguments because &lt;code class=&quot;highlighter-rouge&quot;&gt;fun.arity&lt;/code&gt; returns a &lt;strong&gt;negative&lt;/strong&gt; value. It’s a weird [built-in behavior(https://ruby-doc.org/core-2.2.0/Proc.html#method-i-arity) of procs. There’s a simple fix:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# replace with&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;positive&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, we’ve got a working helper that enables partial application for any function in Ruby. The final result:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enable_partial_application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;positive&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;=&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;=&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;built-in-method&quot;&gt;Built-in method&lt;/h1&gt;

&lt;p&gt;Thankfully, we don’t need to build those helpers. Ruby has a built-in method called &lt;code class=&quot;highlighter-rouge&quot;&gt;#curry&lt;/code&gt;, which works on methods and procs, and does everything I’ve described above.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;z&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;curry&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 8&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_fun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 6&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;plus_two&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; 6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This method takes its name from &lt;a href=&quot;https://en.wikipedia.org/wiki/Currying&quot;&gt;currying&lt;/a&gt;, a process of transforming a single function of N arguments into N functions that only take a single argument. It’s a technique to assist partial application in statically typed functional languages like Haskell, OCaml, and F#. It’s a topic for a separate article so I won’t mention the details.&lt;/p&gt;

&lt;h1 id=&quot;recap&quot;&gt;Recap&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;Partial application helps us fix values and pass fewer arguments&lt;/li&gt;
  &lt;li&gt;We use it quite often, even if we don’t do it explicitly&lt;/li&gt;
  &lt;li&gt;If a function has a variable number of arguments, its arity is negative&lt;/li&gt;
  &lt;li&gt;We need a recursive function to build our own partial application&lt;/li&gt;
  &lt;li&gt;Ruby comes with partial application out of the box: &lt;code class=&quot;highlighter-rouge&quot;&gt;Proc#curry&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Method#curry&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Currying is less performant than plain methods/procs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: if you want to try currying, please keep in mind that it’s not a popular pattern in Ruby, so your colleagues might be skeptical about it. However, I urge you to try it out and compare with the conventional partial application.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Partial_application&quot;&gt;Wikipedia article&lt;/a&gt; on partial application&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://andrewberls.com/blog/post/partial-function-application-for-humans&quot;&gt;Partial function application for humans&lt;/a&gt; by Andrew Berls&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/@tmikeschu/hidden-partial-application-in-ruby-183095540c0e&quot;&gt;Hidden partial application in Ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://ruby-doc.org/core-2.2.0/Proc.html&quot;&gt;Docs for Proc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://morozov.is/images/previews/partial_application.png" /><media:content medium="image" url="https://morozov.is/images/previews/partial_application.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Monad laws in Ruby</title><link href="https://morozov.is/2018/09/08/monad-laws-in-ruby/" rel="alternate" type="text/html" title="Monad laws in Ruby" /><published>2018-09-08T10:00:00+00:00</published><updated>2018-09-08T10:00:00+00:00</updated><id>repo://posts.collection/_posts/2018-09-08-monad-laws-in-ruby.md</id><content type="html" xml:base="https://morozov.is/2018/09/08/monad-laws-in-ruby/">&lt;p&gt;I’ve been using monads in Ruby since May 2016, but I haven’t really understood the theoretical basis for them. I thought about learning Haskell, but I gave up pretty soon: I didn’t think I would benefit from it. Moreover, we started using ReasonML in Planado, which improved my functional programming skills to the point I didn’t really need a new functional language in my life. Why bother with learning Haskell when you know Ruby and Reason, right?&lt;/p&gt;

&lt;p&gt;In early 2018, I became curious about theoretical aspects of functional programming, especially the monad laws. That’s when I realized that I &lt;em&gt;really&lt;/em&gt; needed Haskell, mainly because everyone used it in their articles. It was extremely annoying because I couldn’t even read the code. How was I going to apply those things in Ruby if I can’t even understand what they’re saying? So I got a little help.&lt;/p&gt;

&lt;p&gt;I grabbed my laptop and a friend who knows Haskell and figured out how to describe the three monad laws using Ruby’s &lt;a href=&quot;https://dry-rb.org/gems/dry-monads/1.0/&quot;&gt;dry-monads&lt;/a&gt; gem.&lt;/p&gt;

&lt;!-- excerpt --&gt;

&lt;h2 id=&quot;monads&quot;&gt;Monads&lt;/h2&gt;

&lt;p&gt;Monad is a concept from category theory. Some people describe it as a “monoid in the category of endofunctors”, some call it “computation context”, and some just call them “result objects”. I believe that each of those definitions is correct to some extent. However, neither of them explain the practical side of monads.&lt;/p&gt;

&lt;p&gt;As of September 2018, dry-monads gem contains 5 monads:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; — for nil-safe computations&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; – for expressing errors using types and result objects&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Try&lt;/code&gt; – to describe computations which may result in an exception&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;List&lt;/code&gt; – for idiomatic typed lists&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt; – for asynchronous operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I guess that &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; is the most popular monad in Ruby, especially since railway-oriented programming has become such a hot topic in Ruby. So I will use it to describe what’s going on.&lt;/p&gt;

&lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;

&lt;p&gt;Result, also known as Either, is a monad helpful for building computations that might fail at some point. It is one of the most important parts of railway-oriented programming. Result has two constructors: &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure(a)&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Success(b)&lt;/code&gt;. Both of those constructors encapsulate a value of type &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Result has a lot of useful methods, but there’s one that’s the most important: &lt;code class=&quot;highlighter-rouge&quot;&gt;#bind&lt;/code&gt; – an essential part of monads. It lets us compose computations by applying a block to a value inside the &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;dry/monads/result&apos;&lt;/span&gt;
&lt;span class=&quot;kp&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Monads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mixin&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:number_too_large&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Success(25)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Failure(:number_too_large)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A couple of things to keep in mind when working with &lt;code class=&quot;highlighter-rouge&quot;&gt;#bind&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Failure#bind&lt;/code&gt; doesn’t do anything – it’s a no-op. Use &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure#or&lt;/code&gt; as an alternative.&lt;/li&gt;
  &lt;li&gt;The block &lt;em&gt;must&lt;/em&gt; return a &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt;. Technically, it can return any value – a number, a string, a &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt; monad – but your code will break if you fail to follow the rule.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;three-axioms&quot;&gt;Three axioms&lt;/h2&gt;

&lt;p&gt;Practically, a monad is a data type which obeys three axioms called ”monad laws”:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Left identity: &lt;code class=&quot;highlighter-rouge&quot;&gt;return a &amp;gt;&amp;gt;= f ≡ f a&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Right identity &lt;code class=&quot;highlighter-rouge&quot;&gt;m &amp;gt;&amp;gt;= return ≡ m&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Associativity: &lt;code class=&quot;highlighter-rouge&quot;&gt;(m &amp;gt;&amp;gt;= f) &amp;gt;&amp;gt;= g ≡ m &amp;gt;&amp;gt;= ( \x -&amp;gt; f x &amp;gt;&amp;gt;= g)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those things sound pretty basic when you know Haskell and category theory, but might get extremely complicated if you don’t.&lt;/p&gt;

&lt;p&gt;The first problem I’ve had with those laws: I couldn’t even read them because I didn’t know haskell. Here’s a cheatsheet that helped me read and understand the formulae:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;≡&lt;/code&gt; means that expressions are the same&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt; is a default constructor. For &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;return&lt;/code&gt; is the &lt;code class=&quot;highlighter-rouge&quot;&gt;#Success&lt;/code&gt; method&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;gt;&amp;gt;=&lt;/code&gt; is a bind operator. In Ruby, it’s a method &lt;code class=&quot;highlighter-rouge&quot;&gt;#bind&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;\x -&amp;gt; ...&lt;/code&gt; is an anonymous function. Read &lt;code class=&quot;highlighter-rouge&quot;&gt;-&amp;gt; (x) { ... }&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt; is a function that accepts a value and returns &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt; is a value of type &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;left-identity&quot;&gt;Left identity&lt;/h2&gt;

&lt;p&gt;Left identity is an axiom which states that &lt;code class=&quot;highlighter-rouge&quot;&gt;return a &amp;gt;&amp;gt;= f&lt;/code&gt; is identical to &lt;code class=&quot;highlighter-rouge&quot;&gt;f a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To see what that means, let’s say we have a function &lt;code class=&quot;highlighter-rouge&quot;&gt;f&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are two ways to call use the function:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Call it using plain Ruby&lt;/li&gt;
  &lt;li&gt;Wrap an argument into a monad and pass the function to &lt;code class=&quot;highlighter-rouge&quot;&gt;#bind&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The law says that those are equal:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Success(25)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Success(25)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voilà! That’s it. Putting the value in the default context (&lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt;) and feeding it to a function is the same as applying the function to the value.&lt;/p&gt;

&lt;p&gt;What it means:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;there’s nothing special about &lt;code class=&quot;highlighter-rouge&quot;&gt;#bind&lt;/code&gt; – it’s just a fancy method call&lt;/li&gt;
  &lt;li&gt;if you need to use a monadic function, you don’t need to wrap the argument into a monad&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;right-identity&quot;&gt;Right identity&lt;/h2&gt;

&lt;p&gt;Right identity states that &lt;code class=&quot;highlighter-rouge&quot;&gt;m &amp;gt;&amp;gt;= return&lt;/code&gt; is the same as &lt;code class=&quot;highlighter-rouge&quot;&gt;m&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That means that if we have a &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; object and try to bind it to a &lt;code class=&quot;highlighter-rouge&quot;&gt;#Success&lt;/code&gt;, the operation won’t change anything.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Success(2)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Monads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Success(2)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Failure(2)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I haven’t figured out the practical value of this yet. If you have any ideas, send me an email at &lt;a href=&quot;mailto:igor@morozov.is&quot;&gt;igor@morozov.is&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;associativity&quot;&gt;Associativity&lt;/h2&gt;

&lt;p&gt;The fanciest of the three, associativity axiom states that &lt;code class=&quot;highlighter-rouge&quot;&gt;(m &amp;gt;&amp;gt;= f) &amp;gt;&amp;gt;= g&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;m &amp;gt;&amp;gt;= ( \x -&amp;gt; f x &amp;gt;&amp;gt;= g)&lt;/code&gt; are the same.&lt;/p&gt;

&lt;p&gt;The trickiest part for me was &lt;code class=&quot;highlighter-rouge&quot;&gt;\x -&amp;gt; f x &amp;gt;&amp;gt;= g&lt;/code&gt;, which turned out to be an anonymous function which accepts &lt;code class=&quot;highlighter-rouge&quot;&gt;x&lt;/code&gt; and has a body &lt;code class=&quot;highlighter-rouge&quot;&gt;f x &amp;gt;&amp;gt;= g&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is how the Ruby equivalent of the law would look like:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# prerequisites&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:number_too_large&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# (m &amp;gt;&amp;gt;= f) &amp;gt;&amp;gt;= g&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Success(4)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# m &amp;gt;&amp;gt;= ( \x -&amp;gt; f x &amp;gt;&amp;gt;= g)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; Success(4)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To put it the other way: if you have a chain of computations, it doesn’t matter how you nest them – the result would always stay the same.&lt;/p&gt;

&lt;h2 id=&quot;recap&quot;&gt;Recap&lt;/h2&gt;

&lt;p&gt;A monad is a powerful construct from category theory which can be used as mathematically sound result objects. In Ruby, &lt;a href=&quot;https://dry-rb.org/gems/dry-monads/1.0/&quot;&gt;dry-monads&lt;/a&gt; is the de-facto standard gem, which gives us the &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; (&lt;code class=&quot;highlighter-rouge&quot;&gt;Either&lt;/code&gt;), &lt;code class=&quot;highlighter-rouge&quot;&gt;Maybe&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Task&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Try&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;List&lt;/code&gt; monads.&lt;/p&gt;

&lt;p&gt;To be called a monad, the data type must conform to three axioms called “monad laws”:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Left identity&lt;/strong&gt;: wrapping a value into a monad and binding it to a function is the same as applying the function to the value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Right identity&lt;/strong&gt;: feeding a monadic value to a default constructor doesn’t do anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Associativity&lt;/strong&gt;: you can nest your computations and binds however you like.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While those laws have little to no practical value for a casual user, reading about the principles behind it all might help you join the world of functional programming and category theory. Definitely helps me!&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://fsharpforfunandprofit.com/rop/&quot;&gt;Best resource on Railway Oriented Programming&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://lambdacast.com&quot;&gt;Lambdacast: good podcast for FP beginners&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://miklos-martin.github.io/learn/fp/2016/03/10/monad-laws-for-regular-developers.html&quot;&gt;Miklós Martin on monad Laws&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://learnyouahaskell.com/a-fistful-of-monads#monad-laws&quot;&gt;A fistful of monads&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://wiki.haskell.org/Monad_laws&quot;&gt;Haskell wiki&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-monads/1.0/&quot;&gt;dry-monads&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://trailblazer.to/gems/operation/2.0/&quot;&gt;Trailblazer operations&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/dry-rb/dry-transaction&quot;&gt;dry-transaction&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://morozov.is/images/previews/monad_laws.png" /><media:content medium="image" url="https://morozov.is/images/previews/monad_laws.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Do Notation in Ruby: Railway Oriented Programming</title><link href="https://morozov.is/2018/05/27/do-notation-ruby" rel="alternate" type="text/html" title="Do Notation in Ruby: Railway Oriented Programming" /><published>2018-05-27T10:25:00+00:00</published><updated>2018-05-27T10:25:00+00:00</updated><id>repo://posts.collection/_posts/2018-05-27-do-notation-ruby.md</id><content type="html" xml:base="https://morozov.is/2018/05/27/do-notation-ruby">&lt;p&gt;Railway oriented programming is a design pattern which helps us
handle errors in our applications. Instead of relying on exceptions,
we design our data and functions in a specific way. Since applications
are essentially just a combination of steps, we’ll make some design decisions
about those steps and their structure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There is a &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; type, which can be either a &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt; or a &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt; are practically containers with different data&lt;/li&gt;
  &lt;li&gt;Steps accept &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; and return &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Once a step returns &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;, we stop further execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want to emphasize that &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; is just an alternative name for the &lt;code class=&quot;highlighter-rouge&quot;&gt;Either&lt;/code&gt;
monad. Railway Oriented Programming comes from functional programming,
so it is tightly related to the usual FP concepts like monads, composition, and many
others. However, you don’t need to have an extensive knowledge of monads to
use ROP in your code. In this article, I’ll show you how to write railway-oriented
code in Ruby.&lt;/p&gt;

&lt;!-- excerpt --&gt;

&lt;h2 id=&quot;rop-with-dry-transaction&quot;&gt;ROP with dry-transaction&lt;/h2&gt;

&lt;p&gt;There is a &lt;a href=&quot;http://dry-rb.org/gems/dry-transaction/&quot;&gt;dry-transaction&lt;/a&gt; gem
which provides a DSL to build railway-oriented business transactions.
The core part of the gem is &lt;a href=&quot;http://dry-rb.org/gems/dry-monads/&quot;&gt;dry-monads&lt;/a&gt;
which provides the &lt;a href=&quot;http://dry-rb.org/gems/dry-monads/result/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; type&lt;/a&gt;
and tools to work with it.&lt;/p&gt;

&lt;p&gt;To create a railway-oriented operation, we’ll need to do a few things:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create a class and &lt;code class=&quot;highlighter-rouge&quot;&gt;include Dry::Transaction&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Define a few methods that return either &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Use step adapters to chain those methods together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, we can instantiate the class and pass any input to the &lt;code class=&quot;highlighter-rouge&quot;&gt;#call&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Here’s how it looks like:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyOperation&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Transaction&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Monads&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:validate&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:log&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:persist&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;valid?&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user_age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;something went wrong&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User name is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# some business logic here&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;MyOperation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ^ can return either&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Success(name: ..., age: ...)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# or Failure(&quot;something went wrong&quot;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, we use class-level &lt;code class=&quot;highlighter-rouge&quot;&gt;step&lt;/code&gt; method to compose the &lt;code class=&quot;highlighter-rouge&quot;&gt;validate&lt;/code&gt;,
&lt;code class=&quot;highlighter-rouge&quot;&gt;log&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;persist&lt;/code&gt; methods. &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt; returned from &lt;code class=&quot;highlighter-rouge&quot;&gt;validate&lt;/code&gt; halts the
further execution.&lt;/p&gt;

&lt;p&gt;Pros of the approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It’s plain Ruby&lt;/li&gt;
  &lt;li&gt;It allows us to reuse steps&lt;/li&gt;
  &lt;li&gt;It works!&lt;/li&gt;
  &lt;li&gt;Chained methods don’t need to unwrap the input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons of the approach:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The DSL has a weaker control over the program’s flow — we can’t have conditions unless we add a special step&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; object that we pass around keeps accumulating data and becomes
enormous, so we have to use &lt;code class=&quot;highlighter-rouge&quot;&gt;**rest&lt;/code&gt; in our function signatures&lt;/li&gt;
  &lt;li&gt;Database transactions were hard to implement until &lt;a href=&quot;http://dry-rb.org/gems/dry-transaction/around-steps/&quot;&gt;around step&lt;/a&gt; came around. Still awkward, though&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;dry-monads-to-the-rescue&quot;&gt;dry-monads to the rescue&lt;/h2&gt;

&lt;p&gt;Since dry-transaction is based on dry-monads, we could probably build something
ourselves, right?&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; has a few methods to help us chain those monads:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;bind&lt;/code&gt; applies unwrapped &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt; value to the block, which should return a &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; object. No-op on &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;fmap&lt;/code&gt; is similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;bind&lt;/code&gt;, but wraps the returned value into &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;or&lt;/code&gt; is similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;bind&lt;/code&gt;, but only applies &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt; values&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;or_fmap&lt;/code&gt; is similar to &lt;code class=&quot;highlighter-rouge&quot;&gt;or&lt;/code&gt;, but wraps the returned value into &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tee&lt;/code&gt; does the same thing as &lt;code class=&quot;highlighter-rouge&quot;&gt;bind&lt;/code&gt;, but returns input if the result is a &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;success?&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;failure?&lt;/code&gt; tell us which kind of &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; it is&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;value_or&lt;/code&gt; extracts the value from &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt; or returns fallback value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how the same example would look like using raw monads:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyOperation&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Monads&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;valid?&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user_age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;something went wrong&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User name is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# some business logic here&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The differences:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Plain methods instead of a DSL&lt;/li&gt;
  &lt;li&gt;Better control over flow of our application: more ways to add branching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, there are some disadvantages to the approach&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Having to use &lt;code class=&quot;highlighter-rouge&quot;&gt;#method&lt;/code&gt; is hideous. Solution? Callable objects&lt;/li&gt;
  &lt;li&gt;We still have to pass &lt;strong&gt;all&lt;/strong&gt; parameters to each function&lt;/li&gt;
  &lt;li&gt;Complex logic gets awkward as we add more steps to the chain&lt;/li&gt;
  &lt;li&gt;It doesn’t halt execution if a function returned a &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;, so we’ll have
to work around that&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since 1.0.0.beta1 of dry-monads, there’s a solution to the problems: do notation.&lt;/p&gt;

&lt;h2 id=&quot;do-notation-in-ruby&quot;&gt;Do notation in Ruby&lt;/h2&gt;

&lt;p&gt;When we work with monads and &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; in particular, we have to constantly &lt;code class=&quot;highlighter-rouge&quot;&gt;bind&lt;/code&gt;
them together. When we introduce complex logic with conditions, we end up with
nested binds, and those are hard to work with. Do notation provides an alternative
to &lt;code class=&quot;highlighter-rouge&quot;&gt;bind&lt;/code&gt;, which also flattens the code.&lt;/p&gt;

&lt;p&gt;So we can replace this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With that:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# &amp;lt;= You don&apos;t need to `yield` the last expression&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or even that:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sounds cool, right? We had to use &lt;code class=&quot;highlighter-rouge&quot;&gt;bind&lt;/code&gt; to chain those operations. Now we
can just &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; the steps that can fail and keep the code flat.&lt;/p&gt;

&lt;p&gt;This is how our operation looks with do notation:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyOperation&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Monads&lt;/span&gt;
  &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Monads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;validated_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;validate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;valid?&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;age: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;user_age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Failure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;something went wrong&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;User name is &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# some business logic here&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The core points:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A new mixin: &lt;code class=&quot;highlighter-rouge&quot;&gt;Dry::Monads::Do.for(:call)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; halts the execution if the function returns &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;No need to unwrap the monad: &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; does it for us&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;log&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;persist&lt;/code&gt; no longer need to return &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; as they don’t affect the flow&lt;/li&gt;
  &lt;li&gt;We don’t have to stick to declarative style anymore&lt;/li&gt;
  &lt;li&gt;No need to &lt;code class=&quot;highlighter-rouge&quot;&gt;yield&lt;/code&gt; the last expression — Ruby handles it for you&lt;/li&gt;
  &lt;li&gt;Last expression &lt;strong&gt;must&lt;/strong&gt; return &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;performance&quot;&gt;Performance&lt;/h2&gt;

&lt;p&gt;The reason I wrote the article is that I wanted to benchmark do notation
and compare its performance against dry-transaction.&lt;/p&gt;

&lt;p&gt;The questions I wanted to answer:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Is do-notation faster than dry-transaction?&lt;/li&gt;
  &lt;li&gt;What are the performance differences between happy and not-so-happy paths?&lt;/li&gt;
  &lt;li&gt;What kind of performance drop do we have as we add more steps?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I wrote a &lt;a href=&quot;https://github.com/Morozzzko/benchmarking-dry&quot;&gt;simple benchmark&lt;/a&gt;
to test those things. Design decisions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;No IO or loops&lt;/li&gt;
  &lt;li&gt;Simple arithmetics is good enough&lt;/li&gt;
  &lt;li&gt;Objects behave like pure functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The algorithm I tested looks as follows:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Multiply input by 2&lt;/li&gt;
  &lt;li&gt;If the result is greater than 100, return an error&lt;/li&gt;
  &lt;li&gt;Add 2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total: 3 steps.&lt;/p&gt;

&lt;p&gt;Benchmark output:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;Warming up --------------------------------------
  do-notation: happy    33.809k i/100ms
do-notation: failure    14.274k i/100ms
  transaction: happy     5.878k i/100ms
transaction: failure     5.867k i/100ms
Calculating -------------------------------------
  do-notation: happy    387.914k (± 1.4%) i/s -      1.961M in   5.056134s
do-notation: failure    152.445k (± 1.7%) i/s -    770.796k in   5.057752s
  transaction: happy     59.981k (± 3.0%) i/s -    299.778k in   5.002999s
transaction: failure     60.327k (± 1.5%) i/s -    305.084k in   5.058375s

Comparison:
  do-notation: happy:   387913.7 i/s
do-notation: failure:   152445.2 i/s - 2.54x  slower
transaction: failure:    60327.4 i/s - 6.43x  slower
  transaction: happy:    59981.0 i/s - 6.47x  slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So what do we see:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;dry-transaction performance isn’t really affected by failures&lt;/li&gt;
  &lt;li&gt;do notation becomes approximately 2.5 times slower if we get a &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Do notation is over &lt;em&gt;six&lt;/em&gt; times faster than dry-transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;heavier-benchmark&quot;&gt;Heavier benchmark&lt;/h2&gt;

&lt;p&gt;Alright, so we had a benchmark that worked with three steps that could
&lt;em&gt;theoretically&lt;/em&gt; return &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;. But real-world apps are way more complex
than that. So I decided to add more steps and see what happens.&lt;/p&gt;

&lt;p&gt;Algorithm:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Multiply input by 2&lt;/li&gt;
  &lt;li&gt;Add 2 three times&lt;/li&gt;
  &lt;li&gt;If the result is greater than 100, return an error&lt;/li&gt;
  &lt;li&gt;Add 2 four times&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total: 9 steps.&lt;/p&gt;

&lt;p&gt;Benchmark output:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight not-prose&quot;&gt;&lt;code&gt;Warming up --------------------------------------
  do-notation: happy    10.384k i/100ms
do-notation: failure     9.282k i/100ms
  transaction: happy     2.084k i/100ms
transaction: failure     2.083k i/100ms
Calculating -------------------------------------
  do-notation: happy    108.311k (± 1.3%) i/s -    550.352k in   5.082157s
do-notation: failure     89.917k (± 6.9%) i/s -    454.818k in   5.086821s
  transaction: happy     21.047k (± 2.1%) i/s -    106.284k in   5.052038s
transaction: failure     21.047k (± 1.5%) i/s -    106.233k in   5.048585s

Comparison:
  do-notation: happy:   108310.5 i/s
do-notation: failure:    89917.5 i/s - 1.20x  slower
  transaction: happy:    21047.4 i/s - 5.15x  slower
transaction: failure:    21047.1 i/s - 5.15x  slower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So what do we see &lt;em&gt;here&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Happy path is not that much faster than not-so-happy path&lt;/p&gt;

    &lt;p&gt;That’s because happy path still has to evaluate the remaining steps. It takes time.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;dry-transaction still shows similar performance for both outcomes&lt;/li&gt;
  &lt;li&gt;dry-transaction is five times slower than do notation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;facts--conclusion&quot;&gt;Facts &amp;amp; conclusion&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Railway Oriented Programming is a way to gracefully handle errors in your
application&lt;/li&gt;
  &lt;li&gt;You can use dry-monads and dry-transactions to build railway-oriented services&lt;/li&gt;
  &lt;li&gt;Functions can return either &lt;code class=&quot;highlighter-rouge&quot;&gt;Success&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;Failure&lt;/code&gt;, which form the &lt;code class=&quot;highlighter-rouge&quot;&gt;Result&lt;/code&gt; monad&lt;/li&gt;
  &lt;li&gt;Dry-transaction provides a DSL for railway oriented programming&lt;/li&gt;
  &lt;li&gt;Use &lt;code class=&quot;highlighter-rouge&quot;&gt;bind&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;fmap&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;or&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;or_fmap&lt;/code&gt; to build railway-oriented code in Ruby&lt;/li&gt;
  &lt;li&gt;Use do notation to have a better control over your program’s flow&lt;/li&gt;
  &lt;li&gt;Do notation is &lt;strong&gt;way&lt;/strong&gt; faster and more flexible than dry-transaction&lt;/li&gt;
  &lt;li&gt;This approach is framework-agnostic: works with Rails, Hanami, Sinatra, dry-web-roda&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, there’s a visible lack of documentation for dry-monads, so if you decide to give it a try, you are welcome to contribute!&lt;/p&gt;

&lt;h2 id=&quot;links&quot;&gt;Links&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-transaction&quot;&gt;dry-transaction&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://dry-rb.org/gems/dry-monads&quot;&gt;dry-monads&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://zohaib.me/railway-programming-pattern-in-elixir/&quot;&gt;Railway Oriented Programming in Elixir&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fsharpforfunandprofit.com/rop/&quot;&gt;Slides on ROP&lt;/a&gt; at F# for fun and profit&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://fsharpforfunandprofit.com/posts/recipe-part2/&quot;&gt;Article on ROP&lt;/a&gt; at F# for fun and profit&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://medium.com/pepegramming/do-notation-1e0840a6dbe0&quot;&gt;🇷🇺 Anton Davydov on DO notation &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://morozov.is/images/previews/do_notation.png" /><media:content medium="image" url="https://morozov.is/images/previews/do_notation.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>