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 member | Does |
|---|---|
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
Optionalis empty.dim later as outcome.Optional( of ustring )holds no value; there is no third, uninitialised state. Sometakes its type from its argument.outcome.Some( i )with anint32iis anOptional( of int32 ). A bare literal is aninteger, sooutcome.Some( 42 )does not convert to anOptional( of int32 )(TKO0738); writeoutcome.Some( of int32 )( 42 ).Nonehas no argument to infer from, so its type is always written.Value( )on an emptyOptionalis a bug at the call site, and raises runtime error 1, "illegal function call". Error numbers and handlers shows it being caught. AskHasValue( )first, or useValueOr( ).
When the caller also needs to know why there is no value, use a Result instead.
Activities
- Write
FirstNegative( a() as int32 )returning anOptional( of int32 ), and use it withValueOr. - Rewrite
Averagefrom the section overview to return anOptional( of float64 ).