Function being called but not executing

242 Views Asked by At

I have this custom operator:

infix operator ?> : NilCoalescingPrecedence
func ?> (lhs: Any?, rhs: @autoclosure ()->Any) {
    if lhs == nil {
        print("lhs is nil")
        rhs()
    }
}

Usage:

optional ?> {
    print("executing")
}

The problem is, when the lhs is nil, the closure is not executing. In the console "lhs is nil" is printing, but no "executing" is printing afterwards. How is the "lhs is nil" print statement executed but not rhs?

4

There are 4 best solutions below

0
On BEST ANSWER

The solution seems to be add an overload to the operator with exactly the same signature, except for that it doesn't have the @autoclosure annotation, and the rhs for both has to return Void instead of Any.

infix operator ?> : NilCoalescingPrecedence
func ?> (lhs: Any?, rhs: @autoclosure ()->Void) {
    if lhs == nil {
        print("lhs is nil")
        rhs()
    }
}
func ?> (lhs: Any?, rhs: ()->Void) {
    if lhs == nil {
        print("lhs is nil")
        rhs()
    }
}

This way if I do:

optional ?> doSomething()

the @autoclosure one will get called, regardless of whether doSomething() returns anything or not.

And if I do:

optional ?> {
    doSomething()
    doSomethingElse()
}

the one without the @autoclosure will be called because the closure is of type ()->Void.

0
On

You need to add () at the end of the closure method like below

optional ?> {
    print("executing")
}()
3
On

The thing that causes this behaviour is @autoclosure, if you remove it, it works fine.

This is because @autoclosure will wrap your closure into a closure, so now you have something like this:

{ { print("executing") } }

The outer closure returns Any, right? So it will just return the closure { print("executing") } and do nothing else.

If you want to keep the @autoclosure, you can use the operator this way:

optional ?> print("executing")
0
On

If your intention that curly braces must be added after the operator, you could implement it (without the need of declaring rhs as autoclosure) as:

infix operator ?> : NilCoalescingPrecedence

func ?> (lhs: Any?, rhs: ()->Any) {
    if lhs == nil {
        print("lhs is nil")
        rhs()
    }
}

var myString: String? = ""

// some case that made "string" to be nil...
myString = nil

myString ?> {
    print("executing")
}

However, the purpose of declaring autoclosure is to wrap an expression that’s being passed as an argument to a function:

This syntactic convenience lets you omit braces around a function’s parameter by writing a normal expression instead of an explicit closure.

The Official Swift Documentation - Closures, Autoclosures

which means that there is no need to the curly braces, which should be more natural when working with an operator:

infix operator ?> : NilCoalescingPrecedence

func ?> (lhs: Any?, rhs: @autoclosure ()->Any) {
    if lhs == nil {
        print("lhs is nil")
        rhs()
    }
}

 var myString: String? = ""

 // some case that made "string" to be nil...
 myString = nil

// you could use your operator like:
 myString ?> print("executing")

But Wait!

There is an issue might be faced which is: what if a chunk of code should be added after the operator?

Well, no doubt you have to add the curly braces:

let doSomething = {
    print("executing")
    print("second line of execution")
    print("third line of execution")
}

myString ?> doSomething()