2.2 Either
(require data/either) | package: functional-lib |
The either type provides another implementation of optional values , generally used to represent computations that can fail. However, it augments just and nothing by allowing the kind of failure to be annotated. When a computation results in nothing, it clearly failed, but it is not always clear why (especially after a long chain of monadic computation).
The success constructor is exactly like just—it signals a successful value, and it can be mapped over as a functor or applicative functor and sequenced as a monad. The failure constructor has the same short-circuiting behavior of nothing, but it accepts a value like success, which can be used to annotate the kind of failure.
As an example, we can rewrite the safe- functions from the maybe section using either.
> (define (safe-/ a b) (if (zero? b) (failure "attempted to divide by zero") (success (/ a b))))
> (define (safe-first lst) (if (empty? lst) (failure "attempted to get the first element of an empty list") (success (first lst))))
> (define (safe-rest lst) (if (empty? lst) (failure "attempted to get the rest of an empty list") (success (rest lst))))
> (define (divide-first-two lst) (do [a <- (safe-first lst)] [xs <- (safe-rest lst)] [b <- (safe-first xs)] (safe-/ a b))) > (divide-first-two '(20 11)) (success 20/11)
> (divide-first-two '(3 0)) (failure "attempted to divide by zero")
> (divide-first-two '(3)) (failure "attempted to get the first element of an empty list")
> (success 'hello) (success 'hello)
> (failure 'failed) (failure 'failed)
Either values are monads that short-circuit on failure.
> (map add1 (success 1)) (success 2)
> (map add1 (failure 'failed)) (failure 'failed)
> ((pure +) (success 1) (success 2)) (success 3)
> (do [n <- (success 1)] (pure (add1 n))) (success 2)
procedure
(either failure-proc success-proc either-value) → any/c failure-proc : (any/c . -> . any/c) success-proc : (any/c . -> . any/c) either-value : maybe?
> (either string-length add1 (failure "failed")) 6
> (either string-length add1 (success 1)) 2
> (either string-length add1 (success 2)) 3
procedure
(from-success default-value either-value) → any/c
default-value : any/c either-value : either?
> (from-success #f (failure "failed")) #f
> (from-success #f (success 18)) 18
procedure
(from-failure default-value either-value) → any/c
default-value : any/c either-value : either?
> (from-failure #f (failure "failed")) "failed"
> (from-failure #f (success 18)) #f
procedure
(from-either either-value) → any/c
either-value : either?
> (from-either (failure "failed")) "failed"
> (from-either (success 18)) 18
> (map-failure symbol->string (success 1)) (success 1)
> (map-failure symbol->string (failure 'failed)) (failure "failed")
procedure
(flip-either e) → either?
e : either?
> (flip-either (success 'foo)) (failure 'foo)
> (flip-either (failure 'bar)) (success 'bar)
procedure
(maybe->either x m) → either?
x : any/c m : maybe?
> (maybe->either 'fail (just 42)) (success 42)
> (maybe->either 'fail nothing) (failure 'fail)
procedure
(either->maybe e) → maybe?
e : either?
> (either->maybe (success 42)) (just 42)
> (either->maybe (failure 'fail)) #<nothing>