Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Optional

outcome.Optional( of T ) is a T that might not be there. Returning one puts the possibility of "no answer" into the function's type, so a caller cannot get the value out without deciding what to do when there is none. It lives in the standard library's std/outcome module.

#import "std/outcome"

'' Where needle is in items, if it is anywhere.
function IndexOf( items() as ustring, byref needle as ustring ) _
    as outcome.Optional( of int32 )
    for i as int32 = lbound( items ) to ubound( items )
        if items( i ) = needle then return outcome.Some( i )
    next
    return outcome.None( of int32 )( )
end function

dim planets( 0 to 3 ) as ustring = { "mercury", "venus", "earth", "mars" }

'' Ask first, then take the value.
var found = IndexOf( planets(), "earth" )
if found.HasValue( ) then print "earth is at"; found.Value( )

var missing = IndexOf( planets(), "pluto" )
print "pluto has a value: "; missing.HasValue( )

'' ValueOr supplies a fallback and never fails.
print "pluto is at"; missing.ValueOr( -1 )

'' A new Optional is empty; Clear( ) empties one again.
dim later as outcome.Optional( of ustring )
print "a new Optional has a value: "; later.HasValue( )
later = outcome.Some( planets( 1 ) )
print "now it holds "; later.Value( )
later.Clear( )
print "after Clear: "; later.HasValue( )

Output

earth is at 2
pluto has a value: false
pluto is at-1
a new Optional has a value: false
now it holds venus
after Clear: false
Procedure or memberDoes
outcome.Some( v )an Optional holding v
outcome.None( of T )( )an Optional holding nothing
o.HasValue( )whether there is a value
o.Value( )the value; a runtime error if there is none
o.ValueOr( d )the value, or d when there is none; never fails
o.Clear( )drop the value and become empty

IndexOf can now say "earth is at 2" and "pluto is not here" without inventing an index that means nothing.

A few details matter in practice:

  • A new Optional is empty. dim later as outcome.Optional( of ustring ) holds no value; there is no third, uninitialised state.
  • Some takes its type from its argument. outcome.Some( i ) with an int32 i is an Optional( of int32 ). A bare literal is an integer, so outcome.Some( 42 ) does not convert to an Optional( of int32 ) (TKO0738); write outcome.Some( of int32 )( 42 ). None has no argument to infer from, so its type is always written.
  • Value( ) on an empty Optional is a bug at the call site, and raises runtime error 1, "illegal function call". Error numbers and handlers shows it being caught. Ask HasValue( ) first, or use ValueOr( ).

When the caller also needs to know why there is no value, use a Result instead.

Activities

  1. Write FirstNegative( a() as int32 ) returning an Optional( of int32 ), and use it with ValueOr.
  2. Rewrite Average from the section overview to return an Optional( of float64 ).