dmitri
1
Hi all,
I was expecting that the results of calling _openExistential and using implicit existential opening would be the same, but when I run the following:
func genericFunction<T>(_ v: T) {
print("value: \(v)", "typeUsingTself: \(T.self) typeUsingTypeOf: \(type(of: v))" )
}
func doSomeWork(x: Any) {
_openExistential(x, do: genericFunction)
genericFunction(x)
}
doSomeWork(x: 0)
doSomeWork(x: 0.0)
doSomeWork(x: "zero")
I get:
value: 0 typeUsingTself: Int typeUsingTypeOf: Int
value: 0 typeUsingTself: Any typeUsingTypeOf: Any
value: 0.0 typeUsingTself: Double typeUsingTypeOf: Double
value: 0.0 typeUsingTself: Any typeUsingTypeOf: Any
value: zero typeUsingTself: String typeUsingTypeOf: String
value: zero typeUsingTself: Any typeUsingTypeOf: Any
Does anyone know why simply calling genericFunction(x) won't capture the type inside the function?
jrose
(Jordan Rose)
2
Your generic function has no constraints, so it won’t open any existentials. I think this was done to avoid changing existing code that already passed Any into unconstrained generic positions.
dmitri
3
Is there anything else I could do to capture the type of Any and use the type in a generic context, besides using _openExistential function?
jrose
(Jordan Rose)
4
I don’t believe so as such, but what do you want to do with it that you can’t do with type(of: blah)?
dmitri
5
I want to have the following:
protocol ProtocolA {
associatedtype Value
}
struct StructA<Value>: ProtocolA {
var value: Value
// ...
}
and
func makeA(_ value: Any) -> any ProtocolA {
// create an instance of StructA using an appropriate type
// (not just Any) and return it as any ProtocolA
}
I can easily do that when Value is constrained by some protocol, but what if I wanted Value to be anything, without having to invent a special protocol to make this work?
jrose
(Jordan Rose)
6
I think you’ve found the one case that isn’t covered by the existing rules: wrapping a value in some kind of container. I don’t know of a workaround; maybe somebody more recently working on Swift does.
dmitri
7
This certainly works
protocol ProtocolA {
associatedtype Value
}
struct StructA<Value>: ProtocolA {
var value: Value
}
func genericFunction<T>(_ v: T) -> any ProtocolA {
StructA<T>(value: v)
}
func makeA(x: Any) -> any ProtocolA {
_openExistential(x, do: genericFunction)
}
print(makeA(x: 0))
print(makeA(x: 0.0))
print(makeA(x: "zero"))
but my worry is that some platforms may not allow this due to the _openExistential usage.
1 Like
xwu
(Xiaodi Wu)
8
It doesn’t have anything to do with “capturing”—the static type really is Any, and that’s because genericFunction’s generic parameter has no constraints, so Any is just fine without having to open the existential to pass the argument to the function.
For backward compatibility with versions of Swift before implicitly opened existentials were implemented, this behavior is preserved for Swift 5 even in releases where the feature is now implemented. For the Swift 6 language mode, the existential will be opened as you’re expecting.
See the original proposal text for more explanation here.
4 Likes
Why do you need to pass Any, instead of …?
func makeA(x: some Any) -> any ProtocolA {
genericFunction(x)
}
// or
func makeA<T>(x: T) -> any ProtocolA {
genericFunction(x)
}
dmitri
10
Because in my use cases, I want to be able to pass the result of
if let value = someStruct[keyPath: keyPath] where keyPath is stored as AnyKeyPath.
So
protocol ProtocolA {
associatedtype Value
}
struct StructA<Value>: ProtocolA {
var value: Value
}
func genericFunction<T>(_ v: T) -> any ProtocolA {
StructA<T>(value: v)
}
func makeA(x: some Any) -> any ProtocolA {
genericFunction(x)
}
struct AnotherStruct {
var value: Double
}
let anotherStruct = AnotherStruct(value: 1.0)
let kp: AnyKeyPath = \AnotherStruct.value
if let result = anotherStruct[keyPath: kp] {
print(makeA(x: result))
}
gives me StructA<Any>(value: 1.0), whereas
protocol ProtocolA {
associatedtype Value
}
struct StructA<Value>: ProtocolA {
var value: Value
}
func genericFunction<T>(_ v: T) -> any ProtocolA {
StructA<T>(value: v)
}
func makeA(x: Any) -> any ProtocolA {
_openExistential(x, do: genericFunction)
}
struct AnotherStruct {
var value: Double
}
let anotherStruct = AnotherStruct(value: 1.0)
let kp: AnyKeyPath = \AnotherStruct.value
if let result = anotherStruct[keyPath: kp] {
print(makeA(x: result))
}
produces correct output StructA<Double>(value: 1.0).