Nilable Types
Cobra does 'nil tracking' which forces specification of variables that may be allowed to contain nil.
This is done to
- Help eliminate runtime exceptions about nil reference errors
- Allow reporting of multiple nil referencing errors at compile time (instead of a single failure point at runtime)
- Help document which parameters permit nil and which do not
nil tracking is done by typing the variable as a nilable Type which is by typing to any typeName with a ? suffix
e.g.
str as String = "Mystring" # str can only have a String value
strN as String? # strN may have a String value OR nil
lazyType as Type? # Type OR nil
nilable Typing can be applied to anything that can be Typed ( local variables, class variables Args and returnTypes)
def sendWelcome(cust as Customer, referral as Customer?)
# will not accept nil for `cust`
# will accept nil for `referral`
Casting to and From nilable Types
Sometimes it is necessary to cast an existing typed variable to a nilable type
( e.g for receiving results of a library call) or
to cast a nilable type to its non nilable type (when you've ascertained its not nil and the receiver only takes a non nilable type)
There is a shorthand for both of these
Casting to a nilable version of an existing type
<variable> to ?
Casting away from nilable version
<variable> to !
e.g.
s as String = 'a string'
sn = s to ? # sn is String?
assert sn
s1 = sn to ! # s1 is String - may not get nil
nil and not nil detection
You can explicitly test for nil or not nil values using normal conditionals (true is non-nil)
s as String = 'sss'
assert s
if s
print 's not nil'
sn as String? = nil
assert not sn
if not sn
print 'sn is nil'
nil and non-nil coalesce
There are shortcuts for Nil test and assign default (coalesce) or non nil test and process
a ? b coalesce to b if (a) nil.
The expression evaluates to a unless that value is nil, in which case, it evaluates to b.
Neither expression will be evaluated more than once and if the first expression is non-nil then b will not be evaluated at all.
( Also the direct augmented assignment version a ?= b a gets b if a is nil )
a ! b evaluate b if a not nil.
stuff as String? = nil
defaultStuff='DEFAULT'
stuff = stuff ? defaultStuff # coalesce nil
# OR stuff ?= defaultStuff
# equivalent to stuff = if (not stuff, defaultStuff, stuff)
# or
# if not stuff
# stuff = defaultStuff
assert stuff == defaultStuff
def doSomething(input as String)
if input # detect not nil
print 'Received some input.'
i as int? = input ! .parse(input) # execute non-nil
# equiv to
# i = if(input, .parse(input), input)
# an alternative would also be:
# input = input ? '0'
# i as int = .parse(input)
