Can we unlock non-optional constants / variables in comma separated conditional statements?

The examples you give are pointless — yes, they are valid syntax and do work, but the thing they achieve is meaningless, exactly what if let answer = a is. It’s one thing to be able to do something with the language and another the language itself encouraging these patterns, which is why I said that you’re actively working against it.

To be clear, I would personally love to be able to unwrap non-optionals for practical reasons using the language. However, accepting this change is also changing the meaning of if let — it is no longer about unwrapping optionals, it becomes about binding values in general — and we should be discussing in those terms.

2 Likes

I like this. I have often found myself in a position where I've been forced to nest multiple if let, or write awkward .flatMap-chains or manually promote non-optionals to optionals, just because I needed some intermediate non-optional value in the midst of my if-expression.

2 Likes

Did you know that if case .thing(let val)) = ... expressions are a really elegant way of writing parsers in Swift. For example:

    func parseDxTypedefStruct() -> DxStruct? {
        return rewindStateOnFailure { () -> DxStruct? in
            let docComments = parseDocComments()
            if  case .some(.keyword(.typedef)) = nextToken(),
                case .some(.keyword(.struct)) = nextToken(),
                case .some(.identifier(let id)) = nextToken(),
                case .some(.punctuator(.lBrace)) = nextToken(),
                let members = parseDxStructMembers(),
                case .some(.punctuator(.rBrace)) = nextToken(),
                case .some(.identifier) = nextToken(),
                case .some(.punctuator(.semicolon)) = nextToken()
            {
                return DxStruct(name: id, members: members, docComments: docComments)
            } else {
                return nil
            }
        }
    }
2 Likes

I do not like nested parentheses (and neither does anyone else if SwiftUI and Combine are any indication); ? removes optionality regardless of whether a binding also occurs.

if  case .keyword(.typedef)? = nextToken(),
    case .keyword(.struct)? = nextToken(),
    case .identifier(let id)? = nextToken(),
    case .punctuator(.lBrace)? = nextToken(),
1 Like

Indeed, that is explicitly the case.

As stated before, what you outline as the desired functionality ("unlocking non-optional constants/variables in comma separated conditional statements") already exists in Swift, and it's spelled if case let.

For reasons you've already appreciated, if let would be ambiguous and cannot be permitted. And although if case, guard case, etc., are unique to Swift and not well taught, it's hard to see how another alternative distinct syntax would solve this problem better than, say, improved diagnostics and teaching material.

5 Likes

I think if let with optional promotion would require zero teaching and not violate the principle of least astonishment (POLA). People already expect let to bind the result of an expression to a local scope-bound constant, and Swift already supports optional promotion everywhere else, except for this one place (I think?).

While expert Swift users will be able to find and reach for the case let syntax, it is not easily accessible to beginners, or even experienced programmers. And when one learns about it, one usually also learns about the fact that Optional is really an enum, about optional promotion, patterns matching, and the glaring omission just becomes even more astonishing, in clear violation of POLA.

This proposal is not about adding additional syntax, but removing an exception, and making the existing syntax more consistent across the language. IMHO.

3 Likes

I still think that if let variable? is the better option, but if I'm in the minority on that then I'll support bare if let.

The surprising/astonishing part of the proposal is not the scoped binding, it's that let x = … is a statement not an expression, so if let x = … doesn't make sense without the (implicit) optional unwrapping being interpreted as a boolean expression. You can't write any of the other types of statement in the middle of the conditional either, and I presume you don't want to lift that restriction (even though it might be of minor convenience in some examples).

Just like Objective-C's block syntax, if case let can be hard to get right. And just like for Objective-C's block syntax, there's a helpful site that lists the variations.

1 Like