Skip to content

Using the Either monad

Brian Marick edited this page Oct 24, 2015 · 7 revisions

(The examples below can be found in the examples directory.)

Instead of reporting errors to some out-of-band stream and using a nil return value to short-circuit further computation, you might want to use an Either monad to pass error explanations upstream. You do this by providing different functions to handle the success and error cases.

Here's a simple example, using the recommended setup:

(ns my-types
  (:require [structural-typing.type :as type]
            [structural-typing.assist.oopsie :as oopsie]
            [blancas.morph.monads :as m])
  ...)

(def type-repo
  (-> empty-type-repo
      ...
      (replace-success-handler m/right)
      (replace-error-handler 
        (oopsie/mkfn:apply-to-explanation-collection m/left))))

Now built-like applied to a valid candidate will no longer return it. Instead, it will "wrap" that value in a Right type.

user=> (require '[my.types :as type])
user=> (type/built-like :Point {:x 1, :y 1})
=> Right {:x 1, :y 1}

The case of a type failure is a little more complicated. Internally, the built-like function produces a collection of oopsies. Each of them is about a particular predicate's reason for returning a falsey value. The use of oopsie/mkfn:apply-to-explanation-collection above converts the collection of oopsies into a collection of explanations and then wraps them in a Left.

user=> (type/built-like :Point {:x "1"})
Left (:x should be `integer?`; it is `1` :y must exist and be non-nil)

See monadic-define-1 for the complete definition. See monadic-use for the previous examples, as well as example of using all-built-like to pass judgment on a collection and get error messages indexed to structures that failed.