On this page:
just
nothing
maybe?
just?
nothing?
maybe/  c
maybe
from-just
from-just!
filter-just
map-maybe
false->maybe
with-maybe-handler
exn->maybe

2.1 Maybe

 (require data/maybe) package: functional-lib

The maybe pattern implements optional values, values that represent computations that can fail. Idiomatic Scheme uses #f to represent a “lack of a value”, similar to now null is used in other programming languages, but this exhibits a few problems:

  1. Sometimes #f can be a valid value, at which point it is ambiguous whether or not a result is nonexistent or if it is simply the value #f.

  2. Composing operations that can fail can be tedious and can result in deeply nested conditionals, as each step of the computation must check if the value is #f and short-circuit if necessary.

Maybe reifies the concept of a lack of a value as nothing and the presence of a value as just. It then provides a series of combinators to help work with operations that can fail without excessive error-checking.

Optional values are functors, applicative functors, and monads. This provides a reasonable framework for managing failable computations in a consistent and extensible way. For example, consider an operation that can fail.

> (define (safe-first lst)
    (if (empty? lst)
        nothing
        (just (first lst))))

Now, consider using that operation on a list of characters.

> (safe-first '(#\a #\b #\c))

(just #\a)

> (safe-first '())

#<nothing>

It is possible that you might want to, rather than retrieve the first character, get the first character’s unicode code point. If safe-first returned #f rather than nothing upon failure, you would need to branch to check if the value was found before attempting to convert the character to an integer.

(let ([c (safe-first list-of-chars)])
  (if c
      (char->integer c)
      #f))

It would be possible to use and to make things a little shorter, but the explicit error-checking would still be necessary. However, since optional values are just functors, it is possible to just use map.

> (map char->integer (safe-first '(#\a #\b #\c)))

(just 97)

> (map char->integer (safe-first '()))

#<nothing>

Consider another example: safely dividing a number without having division-by-zero errors. We can implement a safe-/ function like we did with safe-first:

> (define (safe-/ a b)
    (if (zero? b)
        nothing
        (just (/ a b))))

Now, obviously we could use it just like we used safe-first, but what if we want to use them both together? That is, we want to call safe-/ on the result of safe-first. We could try using map again, which seems like it should work:

> (map (λ (x) (safe-/ 2 x))
       (safe-first '(10 20 30)))

(just (just 1/5))

Oops, now we have a just wrapped inside another just. This is because map replaces whatever is inside the functor, not the functor itself, and we returned (just 1/5) from our mapping function. Instead, we want the inner just to be subsumed by the outer one. For that, we can use chain.

The chain function works just like map, but it joins the two wrappers together into a single wrapper after the operation is finished.

> (chain (λ (x) (safe-/ 2 x))
         (safe-first '(10 20 30)))

(just 1/5)

We can use multiple calls to chain to sequence many failable operations at once. For example, we could write a function that divides the first two numbers of a list that won’t ever throw exceptions:

> (define (divide-first-two lst)
    (chain
     (λ (a) (chain
             (λ (xs) (chain
                      (λ (b) (safe-/ a b))
                      (safe-first xs)))
             (safe-rest lst)))
     (safe-first lst)))
> (divide-first-two '(4 3 2 1))

(just 4/3)

> (divide-first-two '(5 0))

#<nothing>

> (divide-first-two '(5))

#<nothing>

> (divide-first-two '())

#<nothing>

It works! That is, itself, kinda cool. Unfortunately, following all the nested calls to chain will very likely make your head spin. That’s where do comes in. The same exact function can be rewritten using do in a much clearer way:

> (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))

(just 20/11)

> (divide-first-two '(3 0))

#<nothing>

Using the monadic interface, we can sequence arbitrary computations that can fail without writing a single line of explicit error handling code.

procedure

(just x)  maybe?

  x : any/c

value

nothing : maybe?

procedure

(maybe? v)  boolean?

  v : any/c

procedure

(just? v)  boolean?

  v : any/c

procedure

(nothing? v)  boolean?

  v : any/c
Value constructors and predicates for optional values. The just function produces a boxed value, and the nothing value represents the absence of a value. Optional values can be serialized with racket/serialize (as long as any nested value is serializable).

> (just 'hello)

(just 'hello)

> nothing

#<nothing>

Optional values are monads that short-circuit on nothing.

> (map add1 (just 1))

(just 2)

> (map add1 nothing)

#<nothing>

> ((pure +) (just 1) (just 2))

(just 3)

> (do [n <- (just 1)]
      (pure (add1 n)))

(just 2)

The nothing binding also serves as a match expander that only recognizes the nothing value, but it must be surrounded with parentheses to be compatible with the syntax of match.

> (define/match (value-or-false mval)
    [((just val))  val]
    [((nothing))   #f])
> (value-or-false (just 'something))

'something

> (value-or-false nothing)

#f

procedure

(maybe/c val-ctc)  contract?

  val-ctc : contract?
Produces a contract that accepts nothing or a just containing a value that satisfies val-ctc.

procedure

(maybe default-value proc maybe-value)  any/c

  default-value : any/c
  proc : (any/c . -> . any/c)
  maybe-value : maybe?
Performs a sort of “first-class pattern-match” on maybe-value—if maybe-value is nothing, then default-value is returned. Otherwise, if maybe-value is (just x), then the result is (proc x).

> (maybe 0 add1 nothing)

0

> (maybe 0 add1 (just 1))

2

> (maybe 0 add1 (just 2))

3

procedure

(from-just default-value maybe-value)  any/c

  default-value : any/c
  maybe-value : maybe?
Equivalent to (maybe default-value identity maybe-value). If maybe-value is nothing, then the result is default-value. Otherwise, if maybe-value is (just x), then the result is x.

> (from-just #f nothing)

#f

> (from-just #f (just "hello"))

"hello"

procedure

(from-just! just-value)  any/c

  just-value : just?
Unwraps an optional value if it is a just?, otherwise raises exn:fail:contract?. Use this function sparingly—it negates much of the benefit of using optional values in the first place, but sometimes there are instances in which the programmer can prove a value will never be nothing, so this function is helpful.

> (from-just! (just "hello"))

"hello"

> (from-just! nothing)

from-just!: contract violation

  expected: just?

  given: #<nothing>

  in: the 1st argument of

      (-> just? any/c)

  contract from:

      <pkgs>/functional-lib/data/maybe.rkt

  blaming: top-level

   (assuming the contract is correct)

  at: <pkgs>/functional-lib/data/maybe.rkt:15.11

procedure

(filter-just maybes-lst)  list?

  maybes-lst : (listof maybe?)
Given a list of optional values, returns a new list with all of the values in the list wrapped with just, discarding all of the values that were nothing.

> (filter-just (list (just 1) nothing (just 3)))

'(1 3)

procedure

(map-maybe proc lst)  list?

  proc : (any/c . -> . maybe?)
  lst : list?
Like map combined with filter-just, but more efficient because there is no need to construct an intermediate list.

> (map-maybe (λ (x) (if (positive? x) (just (sqrt x)) nothing))
             (list -2 3 0 9))

'(1.7320508075688772 3)

procedure

(false->maybe v)  any/c

  v : any/c
Produces nothing if v is #f, otherwise produces (just v). This is useful when interacting with Racket APIs that follow the Scheme convention of using #f as a null value to represent failure or lack of a value.

> (false->maybe #f)

#<nothing>

> (false->maybe "hello")

(just "hello")

syntax

(with-maybe-handler exn-pred? body ...)

 
  exn-pred? : (any/c . -> . any/c)
Executes each body form as usual, but catches any exceptions that satisfy exn-pred?. If such an exception is caught, the result of the whole form is nothing; otherwise, the final body form is evaluated to produce a value, v, and the result is (just v).

This is useful for interacting with Racket APIs that throw exceptions upon failure and adapting them to produce optional values instead.

> (with-maybe-handler exn:fail:contract?
    (bytes->string/utf-8 #"\303"))

#<nothing>

> (with-maybe-handler exn:fail:contract?
    (bytes->string/utf-8 #"hello"))

(just "hello")

procedure

(exn->maybe exn-pred? proc arg ...)  maybe?

  exn-pred? : (any/c . -> . any/c)
  proc : procedure?
  arg : any/c
A procedure version of with-maybe-handler that functions like apply, except that any exceptions thrown during the dynamic extent of the call that match exn-pred? will cause the entire expression to evaluate to nothing. Otherwise, the result is wrapped in just and return as-is.

This can be especially useful when paired with curry, which can be used to produce a wrapped version of a procedure that throws exceptions that instead reports failures in terms of optional values.

> (define try-bytes->string/utf-8
    (curry exn->maybe exn:fail:contract? bytes->string/utf-8))
> (try-bytes->string/utf-8 #"\303")

#<nothing>

> (try-bytes->string/utf-8 #"hello")

(just "hello")