Search code examples
haxe

Meaning of question mark operator '?' before arguments in Haxe


What is the difference between these two function signatures?

function f(?i:Int = 0) {}
function f(i:Int = 0) {}

It doesn't seem to make any difference whether argument is prefixed with ?, both compile fine.


Solution

  • There is indeed no reason to use ? in this example, but there is a difference.

    On a statically typed target, f(null) would not compile since the basic types Int, Float and Bool are not nullable there. However, the ? implies nullability, meaning that

    function f(?i:Int)
    

    is actually the same as

    function f(i:Null<Int> = null)
    

    As you can see, the ? has two effects:

    • A null default value is added, so you can omit i during the call: f();
    • The type is wrapped in Null<T>. While this makes no difference on dynamic targets, it usually has a runtime performance cost on static targets (again: only for Int / Float / Bool arguments).

    The only reason I can think of why you would want arguments with basic types to be nullable is to enable optional argument skipping. When calling f in this example, i can only be skipped if it is nullable:

    class Main {
        static function main() {
            f("Test"); // 0, "Test"
        }
    
        static function f(?i:Int = 0, s:String) {
            trace(i, s);
        }
    }
    

    Note that if you add a default value to an optional argument, that value will be used even if you explicitly pass null:

    class Main {
        static function main() {
            f(); // 0
            f(null); // 0
        }
    
        static function f(?i:Int = 0) {
            trace(i);
        }
    }