Can we do a minor change to swift to treat optional values in dictionary initialisers as "no value"? Example below demonstrates the issue, where instead of a nice and concise first version I have to drop to a more verbose second version which also has an unfortunate consequence that the value becomes "var" (even if it could have been "let"). Besides it's more error prone (e.g. possible to make a typo and use the same key twice, which would be impossible in the first version of the code due to a runtime check for duplicated dictionary keys):
func foobar() {
var foo: String
var bar: String?
var baz: String?
let elements: [String: String] = [
"foo" : foo,
"bar" : bar, // Error: Cannot convert value of type 'String?' to expected dictionary value type 'String'
"baz" : baz // Error: ditto
]
var elements2: [String: String] = [
"foo" : foo
]
elements2["bar"] = bar
elements2["baz"] = baz
// Note that I can but I don't have to do the "if let" dance here, as dictionary
// understands my intent here.
}
Is Attributes useful outside of Dictionary initialization? I'd like to see more work put into literal expressibility, but without that, compactMapValues seems simpler to me.
let elements = Dictionary([
"foo": foo,
"bar": bar,
"baz": baz
])
var foo: String = ""
var bar: String?
var baz: String?
let elements: [String: String] = [
"foo": foo,
"bar": bar, // Error: Cannot convert value of type 'String?' to expected dictionary value type 'String'
"baz": baz // Error: Cannot convert value of type 'String?' to expected dictionary value type 'String'
]
// Workaround #1
let attributes1: Attributes = [
"foo": foo,
"bar": bar,
"baz": bar
]
let elements1: [String: String] = attributes1.dictionary
// Workaround #1can also be turned into a more compact form with the help of `init(_ attributes: Attributes)`
// Workaround #2
let elements2 = Dictionary([
"foo": foo,
"bar": bar,
"baz": baz
])
// Workaround #3
let elements3: [String: String] = [
"foo": foo,
"bar": bar,
"baz": baz
].compactMapValues { $0 }
I'd still say that having this refinement of swift is worthwhile but obviously it's not pressing given we have these great workarounds.
Sometimes you might want a dictionary with optional values, so I wouldn't want to make this the default behavior, and then I think it's pretty subtle if it's based on a contextual type, whether it's explicitly written or not.
It's true (probably in 1% of cases, but still), and the change is not going to amend this. Whether the type annotation is implicit or inferred - the old behaviour is preserved:
var foo: String
var bar: String?
var baz: String?
let elements: [String: String] = [
"foo" : foo,
"bar" : bar, // Error currently
"baz" : baz // Error currently
]
let elements: [String: String?] = [
"foo" : foo,
"bar" : bar, // currently OK
"baz" : baz // currently OK
]
let elements = [ // treated as [String: String?]
"foo" : foo,
"bar" : bar, // currently OK
"baz" : baz // currently OK
]