Throw expressions in ternary operators


(David Hart) #1

I remember discussing something like this in swift-evolution but can’t find it. I was wondering if it was worth a proposal or not.

When constructing objects from dictionary of values, I often write the parsing of optional values as such:

age = try dictionary["age"].flatMap {
  if let age = $0 as? Int {
    return age
  } else {
    throw Error.InvalidFormat
  }
}

Basically, I don’t want to throw if the dictionary does not contain an “age” key, but if it does, I want to throw if it is not an Int. I’m writing this way, but I’d like to write is as such:

age = try dictionary["age"].flatMap { $0 as? Int ?? throw Error.InvalidFormat }

But this causes a compilation error. Don’t you think the ternary operator should allow an expression that throws on the right hand side?

David.


(Krzysztof Siejkowski) #2

But this causes a compilation error. Don’t you think the ternary operator should allow an expression that throws on the right hand side?
For one, ?? is not a ternary operator, ?: is. But I guess it’s just a misspelling.

Anyway, there’re the throwing versions of the ?? operator:

public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T) rethrows -> T
public func ??<T>(optional: T?, @autoclosure defaultValue: () throws -> T?) rethrows -> T?

They’re taking the throwing autoclosure as second parameter. You cannot pass `throw Error.InvalidFormat` directly, because it’s type is not resolved to `() -> throws Int` by compiler. Nos sure if it’s a bug or a feature. In either case, you can work around it by wrapping throw in closure:

let age = try dictionary["age"].flatMap { elem in
try elem as? Int ?? { throw Error() }()
}

You can also simplify it using a throwing function, possibly from the Error enum:

enum Error : ErrorType {
case invalidFormat
static func throwInvalidFormat<T>() throws -> T {
throw Error.invalidFormat
}
}

let age = try dictionary["age"].flatMap { elem in
try elem as? Int ?? Error.throwInvalidFormat()
}

Cheers,
Krzysztof

David.

···

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Hart) #3

Correct, it is not ternary, just a slip of my mind :slight_smile:

let age = try dictionary["age"].flatMap { elem in
    try elem as? Int ?? { throw Error() }()
}

I didn’t know this workaround worked. Cool! Can somebody from the core team tell us if it not supporting throw directly is a bug or an intended feature?


(Krzysztof Siejkowski) #4

Correct, it is not ternary, just a slip of my mind :slight_smile:
I have a great amount of understanding for that, I find `nil coalescing operator` name simply impossible to remember :slight_smile:

I didn’t know this workaround worked. Cool! Can somebody from the core team tell us if it not supporting throw directly is a bug or an intended feature?
Just to elaborate a little after I thought about it for a minute: intended feature. The core reason is that `throw` is a statement (the same way as `if` or `guard` are), and in Swift statements are not expressions. `try`, on the other hand, is an expression.

So you cannot simply `throw` the same way you cannot write:

let elem : AnyObject = “42"
let int = elem as? Int ?? if true { return 42 }
but you can write:

let elem : AnyObject = “42”

let int = elem as? Int ?? { if true { return 42 } }()

There has been already a few discussions whether statements should be expressions in swift or not on the list, which I’ve been only partially following, so I’m not sure if there’re any plans for changing the current statement/expression tradeoff is Swift :slight_smile:

Cheers!

Krzysztof


(Chris Lattner) #5

Correct, it is not ternary, just a slip of my mind :)

I have a great amount of understanding for that, I find `nil coalescing operator` name simply impossible to remember :)

I didn’t know this workaround worked. Cool! Can somebody from the core team tell us if it not supporting throw directly is a bug or an intended feature?

Just to elaborate a little after I thought about it for a minute: intended feature. The core reason is that `throw` is a statement (the same way as `if` or `guard` are), and in Swift statements are not expressions.

Yep, Krzysztof is exactly right here. It would be possible to turn “throw” into an expression - this is precedented in other languages (like C++ iirc).

-Chris

···

On May 2, 2016, at 6:41 AM, Krzysztof Siejkowski via swift-evolution <swift-evolution@swift.org> wrote:

`try`, on the other hand, is an expression.

So you cannot simply `throw` the same way you cannot write:

let elem : AnyObject = “42"
let int = elem as? Int ?? if true { return 42 }
but you can write:

let elem : AnyObject = “42”

let int = elem as? Int ?? { if true { return 42 } }()

There has been already a few discussions whether statements should be expressions in swift or not on the list, which I’ve been only partially following, so I’m not sure if there’re any plans for changing the current statement/expression tradeoff is Swift :)

Cheers!

Krzysztof

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution