Unwrapped Arguments for Functions

@ebg Neither of these particular syntaxes work, the first one because it's used when the closure being invoked is optional, the second because it's used when the return type is optional - therefore neither can distinguish this new use case, because they both already mean something. More importantly though, both syntaxes apply only a single ? to the whole function expression rather than to individual parameters, which limits the feature quite a bit.

I think we're not in need of a new syntax at the moment - we need either an implementation of the proposed syntax to include in a proposal, or a reason why the proposed syntax doesn't work. Either is welcome.

6 Likes

Creating a |? operator and (mis)using the existing throwing mechanism one can achieve a close approximation of the feature.

Remark for this implementation:
Due to the try? I find it nice to see that also functions to the left hand side of the |? operator could not be called. This is in contrast to the proposed solution where there is only a postfix ?.

struct NilError: Error {}

postfix operator |?
postfix func |? <T>(value: T?) throws -> T {
    if let value = value {
        return value
    }
    throw NilError()
}

var array = [4, 2]
var optArray: [Int]? = array
var optIndex: Int? = 1

var value1 = try? array[optIndex|?] // type Int?
// works also with optional chaining
var value2 = try? optArray?[optIndex|?] // type Int?
var value3 = try? optArray|? [optIndex|?] + 1 // type Int?
// multiparameter functions e.g. operators
var value4 = try? optIndex|? + optIndex|? // type Int?
3 Likes

@Qbyte This is brilliant, thank you. To be honest I think this is better than the requested feature, and I’m (almost) satisfied with it. I think it’s better to have to mark the modified function call with try? than to simply pass in optional parameters with an easy to miss question mark. Additionally, it of course already follows the behavior of throwing expressions as per @mayoff's request, since it is one. My only issue with this is the pipe character |. I like having to write try? but I would prefer to be able to use just a postfix question mark for the arguments. For now however, I can live with the pipe. Since posting about this feature I’ve suddenly noticed many more use cases for it in my work, and I’ve been dismayed each time as I realize that it’s not currently supported - but you’ve just given me a solution that I consider viable, so thank you again.

Update of thoughts:

On the other hand, I realize that in reality this operator you've invented is fundamentally different from the unwrapping post-fix operator we're used to (?) - this operator is the Throwing Unwrap Operator, and rather than abort the operation silently upon encountering nil like the normal Unwrap Operator does, it aborts loudly and forces you to handle it as a thrown error. Therefore, I'm considering retracting even my qualm with the pipe character. I'm not sure that I stand by it as the best spelling, but I no longer think that a simple ? would be the better option, because this is a fundamentally different type of unwrap operation and should not be invoked identically.

Update on the update:

After relearning the rules on operator naming I've realized that I don't think there's a better option than what @Qbyte has suggested. The allowed operator characters are / , = , - , + , ! , * , % , < , > , & , | , ^ , ? , and ~, and postfix operators can't begin with either ? or !. It seems clear to me that the operator should be no more than 2 characters, and it also seems clear to me that there should be a question mark involved, since this is an unwrapping operation. We aren't allowed to make the postfix ? operator again, so this suggests that we must have exactly two characters in this operator, so that one can be a ? and the other can distinguish it from the built-in single ? operator. Since postfix operators can't start with a ? or an !, the question mark must be the second character. Therefore, our range of possible operators has gotten quite small. All we can do is pick one of the operator characters besides ? and ! to be the first character of our _? operator. Looking at the options, I now see that | feels like the least weird one to me, so that's what I'll use for now.

Your idea is helpful for 1 optional.
Before your idea, I pitched Add unwrap function to Tuple for unwrapping multiple optionals - #12 by chriskrycho for unwrapping multiple optionals...

@hg-andy-kim I may be misunderstanding you - the given solution works perfectly well for multiple parameters

Wouldn’t it be technically possible (in a future Swift version) to use the ? operator for optional function types (( … ) -> T)? and for function types ( … ) -> T directly? In case of optional function types we would have optional chaining, but in case of a function

func foo(arg1: Type1, … , argN: TypeN) -> T { … }

the expression foo? could then denote the function

func foo?(arg1: Type1?, … , argN: TypeN?) -> T? {
    guard let arg1 = arg1, … , let argN = argN else {
        return nil
    }
    return foo(arg1: arg1, … , argN: argN)
}

or even

func foo?(arg1: Any, … , argN: Any) -> T? {
    guard let arg1 = arg1 as? Type1, … , let argN = argN as? TypeN else {
        return nil
    }
    return foo(arg1: arg1, … , argN: argN)
}

Or is there some ambiguity that I've missed?