Many times I use self-executing closure to assign a value, such as:
let user = {
guard let region = region, let group = group else { return User() }
return User(region: region, group: group)
}()
let suiteName = {
switch Environment.mode {
case .development: return "group.com.example.dev"
case .staging: return "group.com.example.staging"
case .production: return "group.com.example.live"
}
}()
let realm: Realm
do { realm = try Realm() }
catch { return completion(.failure(.databaseFailure(error))) }
A shorthand assignment of these would be very convenient:
let user = let region = region, let group = group
? User(region: region, group: group)
: User()
let suiteName = switch Environment.mode {
case .development: return "group.com.example.dev"
case .staging: return "group.com.example.staging"
case .production: return "group.com.example.live"
}
do let realm = try Realm() catch {
return completion(.failure(.databaseFailure(error)))
}
What do you think.. Swifty or nay?
UPDATE: Disregard guard/do/try from discussion since proposed before.
The first two examples seem to only be held back by the current capabilities of the type checker. There is, however, a want to make control statements (including loops) return values.
So the things you're proposing would be very close to that, though your first option might look a bit different:
let user = if let region, let group = group {
return User(region, group)
} else {
return User()
}
There's probably still design needed for this, like possibly changing return to yield or something, but I think this feature is not exactly a top priority right now :(
I'd be very happy to see control flow statements work as expressions. I'm referring to if/else and switch mostly, I think loops should rather be handled by generators/coroutines, and haven't thought about other types of statement.
I don't know anything about the type checker internals, but I tried around a bit to find the current limitations:
func if_f<T>(_ cond: Bool, then: () -> T, else e: () -> T) -> T {
if cond { return then() } else { return e() }
}
// this works fine
let v = if_f(1 == 2, then: { "lol" }, else: { "lel" })
// this works as well, bc the contextual type from print is Any
print(if_f(1 == 1, then: { "lol" }, else: { 1 }))
// however, this doesn't
let v2 = if_f(1 == 1, then: { "lol" }, else: { 1 })
// unless we give the type checker a little help:
let v3 = if_f(1 == 1, then: { "lol" }, else: { 1 }) as Any
So it might be possible to do at least the simple case, where all branches have the same static type, without type checker improvements?
Wrt syntax, I think using return here would unnecessarily overload the meaning of the keyword. yield should probably be left for coroutines/generators. I'd rather just have no special syntax, the way Rust, Ruby, F# and I'm sure other languages work:
let user = if let region, let group = group {
User(region, group)
} else {
User()
}
let suiteName = switch Environment.mode {
case .development: "group.com.example.dev"
case .staging: "group.com.example.staging"
case .production: "group.com.example.live"
}