Is there any interest in an UnconditionallyInitialisable protocol, or something like it? e.g.:
protocol UnconditionallyInitialisable {
init()
}
It seems trite but I keep running into situations where I really want to express this in generics, e.g. where I want to take something that is a Collection but where I can also create new instances of that type, i.e. Collection & UnconditionallyInitialisable, such as to return a modified copy of an immutable input collection. (bad example - see later posts)
Sometimes I resort to genericising over RangeReplaceableCollection instead, because it happens to incorporate an init requirement for reasons unknown, but lots of useful and common collections don't conform to RangeReplaceableCollection, like Set. In fact it's basically just Array and the string family that do.
At the level of abstraction you're talking about, there's no clear generic meaning for what you get when you call init(). If you want to require a Collection for which you can create new empty instances, then that's a reasonable thing to propose as a generalization of RangeReplaceableCollection.
Can you show a more concrete example? Collection & UnconditionallyInitialisable doesn't provide enough to "return a modified copy of an immutable input collection".
For me it's mostly "collections" in the loose sense, not necessarily things which are RangeReplaceableCollections or even Collections. e.g. a Cache type. But not just collectiony types.
Yeah, I gave a bad example. I wish I'd started this topic a few weeks ago when I last hit this need and the details were fresh in my mind. I know it revolved around collections (as in Collection), but I don't recall how I was expecting to get over that missing universal add method. I might have been envisioning additional new protocols in concert, like an ExtendableCollection or somesuch.
Maybe a better example is a Swift version of Python's defaultdict.
struct DefaultDict<Key: Hashable, Value: UnconditionallyInitialisable> {
…
subscript(_ key: Key) -> Value {
guard let value = storage[key] else {
return storage.insert(key, Value())
}
return value
}
…
}
It's not a particularly compelling example because there's simple workarounds (e.g. have the caller manually provide a constructor closure, or keypath to init), but it's just off the top of my head as one case I recall hitting. Also, I'm not sure how well those workarounds 'compose' with additional layers of generic types, without violating abstractions (some code somewhere has to know the concrete type, or near-to, in every instance in order to provide the necessary type-specific initialisation code…?).
(I know there's a "default:" parameter to Dictionary's subscript, which is very useful sometimes but usually I want the default defined at the object level, not at every lookup)
I agree that there's potential pitfalls to such a broadly-applicable concept. I know some people might not like the idea of Int conforming to this, for example, but there's strong precedence for essentially this in various languages, including Swift with how value types implicitly gain this behaviour. e.g.:
var a: Int // No complaints - defaults to 0.
struct Foo {
let x: Int
}
var b: Foo // No complaints - defaults to { x: 0 }
// _even though_ you can't do `Foo()` explicitly.
Sometimes. I've used ExpressibleByIntegerLiteral before for very similar purposes, for example. But that approach only works for a limited set of types.
Anyway, the purpose of this topic thread is just to see if anyone else has ever had similar desires. I want to see if this is just a me 'problem' or actually appealing to the community, before giving it serious consideration. The solution might not be a truly generic "has-init()" protocol - I want to see what kind of similar use-cases people have. If any.
Trying to use these variables without assigning something to them will fail to compile with a "variable ... used before being initialized" error, so I'm not sure what you mean by this.
Note that python's defaultdict doesn't use a default initializer for a type either - you have to provide the initializer yourself
It's useful because sometimes the default value depends on what you're doing - when multiplying the default should be one, but when adding the default should be zero
I actually expected that error too when I started testing this, but in fact Swift has no complaints, with Swift 5.9:
1> var foo: Int
foo: Int = 0
2> let bar = foo + 1
bar: Int = 1
3> struct Foo {
4. let x: Int
5. }
6> var troz: Foo
troz: Foo = {
x = 0
}
7> troz.x
$R1: Int = 0
8> let zort = troz.x + 2
zort: Int = 2
d = defaultdict(list)
d["key"].append(7)
print(d) // "defaultdict(<class 'list'>, {'key': [7]})"
Technically it's not type-based - the constructor argument is just any callable that returns a value. But it effectively functions as if it were type-based (while allowing more bespoke defaults, such as through a lambda or function).
As I noted, to date in Swift I've just had to do similar, making users pass some 'callable' which can be a closure, function, or a class method like Array.init. I just think it'd be semantically cleaner if they could define a DefaultDict like any other collection, through its primary associated type (and only optionally with a custom constructor callable). e.g. let d = DefaultDict<Array<Int>>().
This is absolutely a bug, and it only works in the REPL (and possibly playgrounds, I didn’t check). It will produce invalid values for something that’s never supposed to have a raw representation of zero, like a non-optional UnsafePointer.