Jon_Shier
(Jon Shier)
1
I'm attempted to create an observables system that can be updated and subscribed to generically. I use something like this:
final class Observer<Value> { }
protocol Observable {
associated type Value
static var observable: Observer<Value>
}
And I can use it fine with normal types:
struct Model: Observable {
static let observable = Observer<Model>()
}
But attempting to make generic types conform:
final class Future<Value> {
static let observable = Observer<Future<Value>>()
}
leads to a simple error: Static stored properties not supported in generic types
Is this a limitation that will be lifted eventually? Any ideas for what I can do in the meantime? Ideally, I'd like to have an API like this:
Model.observerable.observe { }
Future<Model>.observable.observe { }
Otherwise, I'm stuck creating properties for every type of generic I want to observe. Considering I'm using this for networking and other async source, that would be quite a few, so it's not really scalable without some general support. Ideas?
2 Likes
jrose
(Jordan Rose)
2
I think a good part of this restriction came from the fact that it's unclear whether Foo<X>.value and Foo<Y>.value share the same storage. There's actually only one answer that works, but it's still non-obvious enough that we weren't sure if it was a good idea to allow it.
On the implementation side, it's also non-trivial to implement, since new generic types can be created ad infinitum at runtime.
I don't recall any discussions about whether it's a good idea to support; the lack of implementation is the only barrier I know of.
It is possible to implement static stored properties on generic types manually using a global dictionary – albeit with a bit of boilerplate, see:
1 Like
xx-li
(Stellar)
5
From the user's perspective, defining a static constant in a generic is a common scenario and should be well supported.
3 Likes
Since globals are already lazily-initialized, I think the only question here is whether there's value in also supporting the truly-unique case — which would have to be semantically restricted to not refer to the type parameters, of course — in addition to the generic case. But I suppose it's pretty clear from precedent elsewhere that static doesn't mean the former, so yeah, it's just a matter of implementation.
Joe_Groff
(Joe Groff)
7
It seems to me we could potentially hide the true uniqueness behind the ABI (at least when it isn't exposed as a stored property with @_fixed_layout) and treat it as an optimization. If the property is a let and its initializer independent of the type context, we could collapse it into a unique variable.
2 Likes
Would that work for reference holding types?
That could be another limitation on the optimization.
sveinhal
(Svein Halvor Halvorsen)
10
I'd like to replicate this pattern:
extension Notification.Name {
static let myCustomNotification = Notification.Name(...)
}
NotificationCenter.default.post(name: .myCustomNotification)
That is, to be able to define a static let on a type, so I can use the dot-shorthand at call-site.
I'd like to define a generic version of Notification.Name that is generic over a phantom type, so I can do this:
struct TypedNotification<A> {
let name: Notification.Name
}
extension NotificationCenter {
func post<A>(_ note: TypedNotification<A>, object: A) { ... }
func addObserver<A>(_ note: TypedNotification<A>, handler: (A) -> Void) { ... }
}
extension TypedNotification {
// error: Static stored properties not supported in generic types
static let myCustomTypedNotification = TypedNotification<CustomPayloadType>(name: ...)
}
NotificationCenter.default.addObserver(.myCustomTypedNotification) { payload in
// payload has correct type
}
Nevin
11
I also encountered a use for this recently, but I didn’t want to bump the thread. Since it’s already bumped though…
While playing around with some numerical things, I wrote a memoized, recursive, O(log(n)) Fibonacci function using the relations:
F2n = (Fn-1 + Fn+1) · Fn
F2n+1 = Fn2 + Fn+12
First I implemented it concretely for Int:
Fibonacci for Int
private var fibonacciCache: [Int: Int] = [0: 0, 1: 1]
func fibonacci(_ x: Int) -> Int {
if let y = fibonacciCache[x] { return y }
precondition(x >= 0)
let h = x/2
let a = fibonacci(h-1)
let b = fibonacci(h)
let c = a+b
let y = x.isEven ? b*(a+c) : b*b + c*c
fibonacciCache[x] = y
return y
}
let x = fibonacci(92)
print(x) // 7540113804746346429
Then I decided to make it generic over BinaryInteger. My first thought was to store the cache as a static property of a generic type:
struct FibonacciCache<T: BinaryInteger> {
static var cache: [T: T] = [0: 0, 1: 1]
}
But of course that doesn’t work because static stored properties are not supported in generic types.
I ended up hacking together a really ugly workaround, which to spare everyone’s sensibilities I will refrain from posting. Let’s just say it involves [ObjectIdentifier: Any] and a lot of casting. :-)
1 Like
Optional
(你猜我猜不猜你猜)
12
Class stored properties not supported in generic types
mackoj
(Jeffrey Macko)
13
I have encountered this issue recently, I try to implement a type-erased container of one of my protocol that have multiple static properties.
protocol Custom {
static var name: String { get }
var tabBarName: String? { get }
}
struct AnyCustom<Inner : Custom> : Custom {
let value : Inner
static var name: String = Inner.name
var tabBarName: String? { get { self.value.tabBarName } }
init(_ value : Inner) {
self.value = value
}
}
It's not clear from the response above if it will be supported or not. I there a way to go around it ?
michelf
(Michel Fortin)
14
In a case like this you should use a computed property:
static var name: String { get { Inner.name } }
It seems a bit strange that type-erased container would want to use a stored property.
3 Likes
Juanmi
(Juan Miguel Pallares)
16
Generic type-associated stored properties seems like a useful feature. AA tree algorithms use a sentinel node (a type associated property) to avoid constantly checking for null.
1 Like
I just recently came across a need for this and am bumping to show interest in support for it.
1 Like