I'm working on some framework and faced a problem.
I have a public protocol:
public protocol MyPublicProtocol1 {
}
And another one, wich contains a function with generic argument passed. Generic argument has a constraint – argument type must implement the first public protocol:
public protocol MyPublicProtocol2 {
func someFunc<T: MyPublicProtocol1>(completion: (T) -> ())
}
Then I'm implementing my protocols not in public classes. Inside that function with generic argument I have to call another one that takes not generic argument and look like that:
func anotherFuncWith(completion: (MyPublicProtocol1) -> ())
And here's what implementation looks like:
class MyPublicProtocol1Impl: MyPublicProtocol1 {
}
class MyPublicProtocol2Impl: MyPublicProtocol2 {
func someFunc<T: MyPublicProtocol1>(completion: (T) -> ()) {
anotherFuncWith(completion: completion)
}
}
And of course I have an error in the last string.
I can't declare someFunc(completion:) with not a generic argument like:
func someFunc(completion: (MyPublicProtocol1Impl) -> ())
Because MyPublicProtocol1Impl class mustn't be public. And I also can't declare anotherFuncWith(completion:) to take generic argument too for some reasons.
Is there a way to somewhat "convert" (T: MyPublicProtocol1) -> () completion to be just a (MyPublicProtocol1) -> ()?
Any help or advices are very appreciated! And thank you for reading my story!
You've asked for something that is not provably true. You have a method:
func anotherFuncWith(completion: (MyPublicProtocol1) -> ())
This accepts a method that can receive any MyPublicProtocol1. You then pass it a method of type:
(T: MyPublicProtocol1) -> ()
anotherFuncWith may pass something that is not T, at which point this is undefined. To make it more concrete, let's get rid of most of the stuff here and make MyPublicProtocol1 be Any (just to pick a trivial protocol).
func anotherFuncWith(completion: (Any) -> ()) {
completion("This is a string which is an Any, so that's fine")
}
func someFunc<T: Any>(completion: (T) -> ()) {
anotherFuncWith(completion: completion)
}
This fails to compile exactly like your example. Now let's think through what I could do if it did compile. I could call:
func complete(x: Int) -> () {
print(x + 1)
}
someFunc(completion: complete)
So now anotherFuncWith calls complete passing a String, which can't be added. Crash.
The underlying problem here is that you've gotten covariance and contravariance backwards.
How do we fix it? Depends on what you really mean. This code is a little strange. Do you care about the actual type of T or not? You never seem to use it. If you don't care, then just use protocols:
public protocol MyPublicProtocol2 {
func someFunc(completion: (MyPublicProtocol1) -> ())
}
If you do care about the actual type, use a PAT:
public protocol MyPublicProtocol2 {
associatedtype T: MyPublicProtocol1
func someFunc(completion: (T) -> ())
}
Or you may want to rethink whether you need a protocol here at all. I often find people reach for protocols when they don't need them yet. Do you have multiple implementations of these protocols? Do you have multiple types that are passed? If not, I'd simplify and only go generic/protocol when you have a real problem you're solving in the current code. (You may need them; this is just my stock advice that many people have found useful when they've over-designed.)
The dirty way to get around this is
func someFunc<T: MyPublicProtocol1>(completion: (T) -> ()) {
anotherFuncWith { (thing) in
if let tThing = thing as? T {
completion(tThing)
}
}
}
I would only do this if you are very sure of the code surrounding it as it is certainly fallible.
Also, this works too. I'm unsure what you're actually trying to do so I'm not sure if it solves your problem
func anotherFuncWith<T: MyPublicProtocol1>(completion: (T) -> ()) {
}
class MyPublicProtocol2Impl: MyPublicProtocol2 {
func someFunc<T: MyPublicProtocol1>(completion: (T) -> ()) {
anotherFuncWith(completion: completion)
}
}
Related
Please consider the following code:
protocol P {}
class X {}
class Y: P {}
func foo<T>(_ closure: (T) -> Void) { print(type(of: closure)) }
func foo<T>(_ closure: (T) -> Void) where T: P { print(type(of: closure)) }
let xClosure: (X?) -> Void = { _ in }
foo(xClosure) // prints "(Optional<X>) -> ()"
let yClosure: (Y?) -> Void = { _ in }
foo(yClosure) // prints "(Y) -> ()"
Why does the foo(yClosure) call resolve to the version of foo constrained to T: P?
I understand why that version prints what it prints,
what I don't see is why it gets called instead of the other one.
To me it seems that the non-P version would be a better match for T == (Y?) -> Void.
Sure, the constrained version is more specific, but it requires conversion
(an implicit conversion from (Y?) -> Void to (Y) -> Void),
while the non-P version could be called with no conversion.
Is there a way to fix this code in a way such that the P-constrained version gets called
only if the parameter type of the passed-in closure directly conforms to P,
without any implicit conversions?
Specificity seems to always trump variance conversions, according to my experiments. For example:
func bar<T>(_ x: [Int], _ y: T) { print("A") }
func bar<T: P>(_ x: [Any], _ y: T) { print("B") }
bar([1], Y()) // A
bar is more specific, but requires a variance conversion from [Int] to [Any].
For why you can convert from (Y?) -> Void to (P) -> Void, see this. Note that Y is a subtype of Y?, by compiler magic.
Since it is so consistent, this behaviour seems to be by design. Since you can't really make Y not a subtype of Y?, you don't have a lot of choices if you want to get the desired behaviour.
I have this work around, and I admit it's really ugly - make your own Optional type. Let's call it Maybe<T>:
enum Maybe<T> {
case some(T)
case none
// implement all the Optional methods if you want
}
Now, your (Maybe<Y>) -> Void won't be converted to (P) -> Void. Normally I wouldn't recommend this, but since you said:
in the real-world code where I encountered this, the closure has multiple params, any of them can be optional, so this would lead to a combinatorial explosion.
I thought reinventing Optional might be worth it.
I'm switching over a project from a custom Result type to the native Swift Result type and keep running into this - or similar - error messages:
Member 'success' in 'Result<_, Error>' produces result of type 'Result<Success, Failure>', but context expects 'Result<_, Error>'
protocol DataFetcher {
func request<T>(completion: #escaping (Result<T, Error>) -> Void )
}
struct RandomFetcher: DataFetcher {
func request<String>(completion: #escaping (Result<String, Error>) -> Void) {
let str = "Hello"
completion(.success(str))
}
}
The idea is to have make a bunch of generic Fetchers for different data extraction calls and to pass these to VC's who would have a var fetcher: DataFetcher property. The VC's know which concrete types they expect from their request. I can't use an associated type as I need to save a bunch of these in an array and I thought I could get away with just the generic implementation - but it almost seems as if the Result type being declared as a generic in the protocol, means that it won't accept when I specify it in the implementation. What am I missing?
func request<String>(completion: #escaping (Result<String, Error>) -> Void) {
This is a classic generics mistake in Swift. This does not mean "request requires T==String." This means "there is an arbitrary type called String that this function accepts." There is no relationship between this String and Swift.String.
What you're trying to do here violates the protocol. The protocol says that the caller gets to pick any T they want. You can't conform to that protocol with a restricted T.
If you want the conforming type (RandomFetcher) to get to decide the type of T, then you have to use an associatedType (that's what associatedType means).
The thing you're trying to build is pretty common, but not trivial, and requires a different way of thinking about the problem. I walk through it step-by-step in this series.
In this case an associated type is preferable
protocol DataFetcher {
associatedtype FetchType
func request(completion: #escaping (Result<FetchType, Error>) -> Void )
}
struct RandomFetcher: DataFetcher {
func request(completion: #escaping (Result<String, Error>) -> Void) {
let str = "Hello"
completion(.success(str))
}
}
I'm implementing a class which needs to conform to a protocol that looks like this:
protocol P {
func magic<T>(_ thing: Thing, as type: T.Type) -> T
}
This protocol is provided by third-party code, which means I can't change it in any way in order to solve this problem.
Now, I have a generic function
func magicForObject<T: AnyObject>(_ thing: Thing, as type: T.Type) -> T
and I want to call it from within my implementation of magic, just for input things which are in fact objects.
That is, I want to do something like this:
func magic<T>(_ thing: Thing, as type: T.Type) -> T {
if T.self is AnyClass {
return magicForObject(thing, as: type)
}
else {
// do something else
}
}
but I can't find any way to make this work. The code above obviously doesn't compile, and neither does stuff like
if let T_ = T.self as? AnyClass { ... }
because T_ is just a normal variable, not a generic parameter (which is presumably compile-time).
I also tried doing this:
func magic<T: AnyObject>(_ thing: Thing, as type: T.Type) -> T { ... }
func magic<T>(_ thing: Thing, as type: T.Type) -> T { ... }
and implementing the two separately. The constrained AnyObject one is correctly called if calling this function directly on the object, but not when the object is cast to protocol type P, in which case the second one is always used.
This situation seems hopelessly constrained, but are there any workarounds I haven't thought of?
Update
It looks like this isn't currently possible in Swift. I've made a post pitching the idea in the Swift forums; please feel free to chime in if this is something you also need.
Your example is kind of hard to work with, so I had to do a lot of assumptions, but I guess this should do it for what you are requiring.
From what you said you have a given protocol P:
protocol P {
func magic<T>(_ thing: Thing, as type: T.Type) -> T
}
Lets give P a default implementation of what you need it to do:
extension P {
// Implementation for magic where T is a class
func magic<T>(_ thing: Thing, as type: T.Type) -> T where T: AnyObject {
print("AnyObject Called")
return Test2() as! T
}
// Implementation for magic where T is a struct
func magic<T>(_ thing: Thing, as type: T.Type) -> T {
print("Struct Called")
return Test() as! T
}
}
You have a class that will conform to P
class Test2: P {
}
Lets assume you have this Thing object and a struct we want to pass to see if we have the right results:
class Thing {
}
struct Test {
}
Now lets test if we call magic on Test2 if it will call the right function accordingly to what type is passed to magic
let test = Test()
let test2 = Test2()
// T is Test2 so its a class
test2.magic(Thing(), as: Test2.self)
// T is Test so its a struct
test2.magic(Thing(), as: Test.self)
The Print Output calls
AnyObject Called
Struct Called
which seems like you could do something for structs and another thing for classes
I think best if I start with an example:
class Test<T> {
func test(closure: (T) -> Void) { }
func test(closure: (T) -> T) { }
func test(closure: (T) -> Test<T>) { }
}
Test<Int>().test { a in }
The code above gives the following error:
error: ambiguous use of 'test'
This is because the Swift compiler doesn't know to which one of the three methods is should map the call to. But from the closure body it's quite clear that it returns a Void, so it should pick the first method.
Looks like the Swift compiler cannot determine to which method overload to map the call to based on the closure body. If I explicitly specify the closure signature, then the problem goes away:
Test<Int>().test { (a: Int) -> Void in }
My question is: can I somehow instruct Swift to pick-up the correct overload for short-hand closure expressions like the one in discussion, or will I have to explicitly declare the closure signature?
Actually, it seems that I was pushing the compiler limits too hard (as #Martin R pointed in the comments). The { a in } closure was kinda incomplete, since the compiler had no statements to infer the closure return type from.
This works:
Test<Int>().test { (a: Int) -> Void in () }
Same as the following:
func doNothing() { }
Test<Int>().test { (a: Int) -> Void in doNothing() }
In the above examples the compiler is provided with the minimum amount of information to determine which overload to pick.
In Swift, one should use type of parameters or return value to implicitly specialize a generic function. The problem is, when I call function like this:
func serialize<T>(continuation: GenericWithLongName<T, NSError> -> Void) -> Void
I cannot just write
serialize<SomeType> {
obj in
...
}
It should be
serialize {
(obj: GenericWithLongName<SomeType, NSError>) -> Void in
...
}
which looks painful.
It seems this "feature" exists for a long time. Is it a design decision? Is there any negative implication from allowing explicitly specialization?
And is there any way to make code above neat and clean without refactoring that generic class?
One way to "specialize" the function is by including the generic type as a function parameter:
func serialize<T>(
t: T.Type,
continuation: GenericWithLongName<T, NSError> -> Void ) -> Void { }
Now you can "specialize" the function like this:
serialize(SomeType.self) {
obj in
...
}
I don't know the answer to why your requested feature is not available. I agree that the feature you recommend would be useful, but in the meantime this works just as well and is almost as concise.