1:- module(maybe, [ call_maybe/3
    2                 , is_just/1
    3                 , is_nothing/1
    4                 , just_value/2
    5                 , maybe_value/2
    6                 , maybe_list/2
    7                 , maybe_default_value/3
    8                 , default_maybe_value/3
    9                 , map_maybe/3
   10                 , fold_maybe/4
   11                 ]).   12:- use_module(library(error), [ domain_error/2 ]).   13
   14
   15:- multifile error:has_type/2.   16error:has_type(maybe, Maybe) :-
   17    nonvar(Maybe),
   18    memberchk(Maybe, [nothing, just(_)]).
   19error:has_type(maybe(T), Maybe) :-
   20    error:has_type(maybe, Maybe),
   21    ( Maybe = just(X) ->
   22        error:has_type(T, X)
   23    ; true
   24    ).
   25
   26% we don't load library(quickcheck) to avoid a dependency
   27:- multifile quickcheck:arbitrary/2.   28quickcheck:arbitrary(maybe, Maybe) :-
   29    quickcheck:arbitrary(maybe(any), Maybe).
   30quickcheck:arbitrary(maybe(T), Maybe) :-
   31    random_between(0, 4, N),
   32    ( N == 0 ->
   33        Maybe = nothing
   34    ; % otherwise ->
   35        quickcheck:arbitrary(T, X),
   36        Maybe = just(X)
   37    ).
   38
   39:- multifile quickcheck:shrink/3.   40quickcheck:shrink(maybe(T), just(X0), just(X)) :-
   41    quickcheck:shrink(T, X0, X).
 is_just(+Maybe:maybe) is semidet
True if Maybe is just(_). Useful for include/3.
   47is_just(just(_)).
 is_nothing(+Maybe:maybe) is semidet
True if Maybe is nothing. Useful for exclude/3.
   53is_nothing(nothing).
 just_value(+Just:maybe(T), -Value:T) is det
just_value(-Just:maybe(T), +Value:T) is det
True if Just wraps Value. Throws an exception if Just is nothing.
   61just_value(just(Value), Value) :-
   62    !.
   63just_value(nothing, _) :-
   64    domain_error(just, nothing).
 maybe_value(+Maybe:maybe(T), -Value:T) is det
True if Maybe is just(Value); fails for nothing.
   70maybe_value(just(Value), Value).
 call_maybe(:Goal, ?Value:T, -Maybe:maybe(T)) is multi
If call(Goal) succeeds, Maybe=just(Value), otherwise Maybe=nothing. Goal typically binds Value. If Goal produces multiple solutions on backtracking, so will call_maybe/3. In that case, Maybe will never be nothing.

For example,

?- L = [a,b,c], call_maybe(L=[H|_], H, MaybeHead).
H = a,
MaybeHead = just(a).

?- L = [], call_maybe(L=[H|_], H, MaybeHead).
MaybeHead = nothing.

Of course, that particular example could have been implemented with maybe_list/2 instead.

   90:- meta_predicate call_maybe(0,?,-).   91call_maybe(Goal, Value, Maybe) :-
   92    ( call(Goal) *->
   93        just_value(Maybe, Value)
   94    ; % no solutions ->
   95        Maybe = nothing
   96    ).
 maybe_list(?Maybe:maybe(T), ?List:list(T)) is det
Relates a List to a Maybe value. An empty list is equivalent to nothing. A non-empty list is equivalent to just(Head).
  103maybe_list(nothing, []).
  104maybe_list(just(H), [H|_]).
 maybe_default_value(+Maybe:maybe(T), +Default:T, -Value:T) is det
maybe_default_value(-Maybe:maybe(T), +Default:T, +Value:T) is multi
True if Maybe wraps Value with a Default for the nothing case.
  111maybe_default_value(nothing, Default, Default).
  112maybe_default_value(just(Value), _, Value).
 default_maybe_value(+Default:T, +Maybe:maybe(T), -Value:T) is det
default_maybe_value(+Default:T, -Maybe:maybe(T), +Value:T) is multi
Like maybe_default_value/3 with different argument order. This argument order is convenient for maplist/3 like so:
maplist(default_maybe_value(7), Maybes, DefaultedValues).
  121default_maybe_value(Default, Maybe, Value) :-
  122    maybe_default_value(Maybe, Default, Value).
 map_maybe(+Goal, ?Maybe0:maybe, ?Maybe:maybe)
True if call(Goal, Value0, Value) succeeds for just values. Goal is not called for nothing values, which remain unchanged. "Use the source" for a clearer explanation.
  130:- meta_predicate map_maybe(2,?,?).  131map_maybe(_, nothing, nothing) :-
  132    !.  % help indexer
  133map_maybe(Goal, just(V0), just(V)) :-
  134    call(Goal, V0, V).
 fold_maybe(+Goal, ?Maybe:maybe, ?Accum0, ?Accum)
nothing leaves Accum0=Accum while just relates them via Goal. "Use the source" for a clearer explanation.
  141:- meta_predicate fold_maybe(3,?,?,?).  142fold_maybe(_, nothing, Accum, Accum).
  143fold_maybe(Goal, just(Value), Accum0, Accum) :-
  144    call(Goal, Value, Accum0, Accum)