Wiki

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)