A More Swifty Way to Check an Optional for a Nil Value

It was removed due to how confusing it is to use with optional Bool values.

3 Likes

Gotcha

My suggestion:

ifso someOptional { }

ifno someOptional { }

ifso is short for: if set optional

ifno is short for: if nil optional

Another thought I had:

if Bool(someOptional) { }

We sort of have this now with swift 5.7:

var foo: String?

guard let foo else { return }

print(foo.isEmpty)

// or

if let foo {
    print(foo.isEmpty)
}
2 Likes

I should probably use the guard statement more often; but sometimes
I feel that I just need the simple nil check, and not necessarily do
anything with the optional. I also may want to run some code if a
condition is met, but not otherwise – and without having to exit out
of a scope, or to create a new function.

updated my previous comment.

You can use this new syntax with if let statements as well.

Thanks, leaving out the assignment is obviously a very recent
addition that I didn't know about. It makes it somewhat swiftier.

The only crux would be: "Changes you make to the variable inside
the body of the if statement apply only to that local variable,
not to the original, optional constant or variable that you
unwrapped."

Yet another thing I could propose (which includes replacing some
of the comparison and logical operators) would turn something
like this ...

if var1 != nil && number == 0 || !someBool { }

into this:

if ok var1 and number equals 0 or not someBool { }

This could also allow the exclamation mark to be used
only for optional unwrapping.

Yea, it doesn’t cover all cases.

For example if you want to access a variable within another struct you still need to do:

if/guard let unwrapped = myStruct.myOptional {
    // do something with the optional
}

But I can see this being very useful for the good old if let self = self dance we do in closures.

You can trivially create this initializer on Bool yourself.

For my latest proposal I just wanted to add that "equals" is a bit
long and could be replaced with "be", like this:

if ok var1 and number be 0 or not someBool { }

It becomes a bit like natural-language programming.

Yes, it would be nice to have some custom operators made for this.
I should definitely learn more about that.

Here's a little report on the things I've tried:

This, added at the top level, seems to do the trick:

extension Bool {
    init(_ opt: Optional<Any>) {
        self = opt != nil
    }
}

Now we can write:

if Bool(someOptional) { } // instead of: if someOptional != nil { }

if !Bool(someOptional) { } // instead of: if someOptional == nil { }

Using prefix custom operators was the next thing I tried.
It seems that it's not possible to define a custom operator
that contains ordinary letters, so, for example, "and" cannot
be used, but some other signs are possible. This led me to
come up with the following (to be put on the top level):

prefix operator <>

extension Optional {
    static prefix func <>(opt: Optional<Wrapped>) -> Bool {
        return opt != nil
  }
}

prefix operator <!>

extension Optional {
    static prefix func <!>(opt: Optional<Wrapped>) -> Bool {
        return opt == nil
  }
}

Now we can write:

if <>someOptional { } // instead of: if someOptional != nil { }

if <!>someOptional { } // instead of: if someOptional == nil { }

A caveat here is that I don't know how it affects performance,
and I assume there's a risk that being allowed to define
operators like this could be removed in the future.

By the way, I also tried to see if there was some preprocessor
directive that I could use, so that I could write, for example,
"and" instead of &&, but alas, there doesn't seem to be much of
that in Swift.

Agree. The current syntax is clear, avoids special new keywords, avoids adding more contexts to existing operators, avoids expanding if statements to checking if a value not nil as well as true. I could be wrong that most of the time in if statements, we check for is not nil by binding to a variable.

If an optional happens to also wrap a Bool it can create ambiguity for both the compiler and the reader and although this may be rare, it seems unsafe in violation of a core value of swift:

I’m glad this was all discussed but it appears that the current solution is the swifty approach all along.

If someone reads your message, they would get the impression
that it is "unsafe" to do it like I suggested. I certainly don't want
it to be unsafe, so let's sort things out. Regarding the wrapped
Bool, do you mean something like this:

var someBool: Bool? = true

if <>someBool { Swift.print("\(someBool!)") } // prints "true"

That is, you create a new non-optional Bool that is discarded once
the if statement has been evaluated. It seems clear to me.

When you say that the current syntax avoids expanding if statements,
is that because you worry about performance?

I have now replaced 421 of these nil checks in my current code.
I haven't noticed any difference in performance. It all works
like a charm so far, and the code looks so much nicer than before.
I haven't done any serious timing checks, though.

I prefer not to define variables inside statements, but that is my
way of doing things.

I think using <> and <!> is the swiftiest way to do it presently.

I'm not forcing anyone else to use it – it's strictly optional :wink:

If the optional someBool is e.g. a computed property, it can be non-nil when evaluating the if-condition, and nil inside the true-branch, causing a crash.

However, optional binding will perform a both nil-check, an unwrap operation, and bind to a local let guaranteed to be non-nil, all in one go!

1 Like

Or a weak property pointing to an object that gets deinited between the check and the unwrap.

1 Like

I see – very interesting, thank you. I guess the != nil
is no better or worse than <> in this regard, meaning that
the same caution must be applied when using != nil ?

Yes.

I appreciate your help. I conclude:

Whenever it is safe to use != nil or == nil, it should
also be safe to instead use <> or <!>, respectively.

By the way, here's a mnemonic that can be used for <>:

"A diamond has a value"

Not that anyone would be interested, but I am slowly coming
to accept the "if let" statement – especially if it can be simplified
like in Swift 5.7. It does wipe out a lot of exclamation marks in
the code (woo-hoo).

As I understand it, there are still a lot of situations where an
explicit check for nil would be needed, and then it is a personal
choice whether to use != nil, or possibly a custom operator like <>.

An example:

var someInt: Int? = 5

if someInt != nil { someInt = 10 } // this changes someInt

Currently I don't see how something like this could be gracefully
replaced with an "if let" or "if var". The problem is that the original
someInt is not accessible between the curly braces. The new
variable/constant prevents access to the original:

var someInt: Int? = 5

if var someInt { someInt = 10 } // this does nothing

if var someInt = someInt { someInt = 10 } // this does nothing

This works, but it creates an unnecessary constant:

if let wastedInt = someInt { someInt = 10 } // this changes someInt

In case I have misunderstood something, or you have some other
swifty solution, let me know.

P.S. I just realized it could be done like this:

if let _ = someInt { someInt = 10 } // this changes someInt

Is there a use case for this? I'm having a hard time imagining it.

Assigning only non-nil values, that I've seen to be useful.

infix operator =?: AssignmentPrecedence

public extension Optional {
  static func =? (optional0: inout Self, optional1: Self) {
    if let optional1 {
      optional0 = optional1
    }
  }

  static func =? (wrapped: inout Wrapped, optional: Self) {
    if let optional {
      wrapped = optional
    }
  }
}
2 Likes