Swift 3: Inheritance from non-named type - swift

I have the following SSCIE:
protocol Foo {
associatedtype Bar
associatedtype Baz: (Self.Bar) -> Void
var currentValue: Bar { get }
}
That I want to use like this:
func call<T: Foo>(foo: T, callback: #escaping T.Baz) {
DispatchQueue.main.async {
callback(foo.currentValue)
}
}
But it fails to compile, with the error:
Inheritance from non-named type '(`Self`.Bar)'
This also fails to compile when I use (Bar) -> Void and (Foo.Bar) -> Void.
Sadly, Googling this didn't come up with any useful results.
Does anyone have any idea what this error means, what I'm doing wrong, and how to correct it?

Associated types in Swift 3 can only be "is-a"-constrained. So your Bar is required to be an Any. Which, by the way, is not much of a constraint ;). In other words, you can remove it.
However, (Self.Bar) -> Void is a function type and you can't constrain an associated type like this.
If you want to define a callback type, you can use a typealias:
protocol Foo
{
associatedtype Bar
typealias Callback = (Self.Bar) -> Void
var currentValue: Bar { get }
func f(callback: Callback) -> Void
}
Using #escaping does not currently work in a typealias (see SR-2316 and its various duplicates). This is a bug that was supposed to have a fix soon (as of August 2016). So you will have to spell it out for now:
func call<T: Foo>(foo: T, callback: #escaping (T.Bar) -> Void) {
DispatchQueue.main.async {
callback(foo.currentValue)
}
}
Update: As Hamish suggested, I filed SR-4967. I'll update this post as soon as there is any news about it.

As already mentioned, function types can't be used as associated types.
Try this instead:
func call<T: Foo>(foo: T, callback: #escaping (T.Bar) -> Void) {
...
}
and using this design you can mix-and-match function types (for the callback arg) for each specific helper function (call in your example) you come up with.

Related

Swift generic constraint on method does not behave as expected when called from instance

Here is the following piece of Swift code:
class HTTP {
func run<T: Decodable>(handler: (Result<T, Error>) -> Void) {
HTTP.handle(handler: handler)
}
}
extension HTTP {
static func handle<T: Decodable>(handler: (Result<T, Error>) -> Void) {
Swift.print("Base")
}
}
extension HTTP {
static func handle<T: Decodable & Offline>(handler: (Result<T, Error>) -> Void) {
Swift.print("Offline")
}
}
protocol Offline {
associatedtype Data
var data: Data { get set }
}
struct Model: Decodable, Offline {
var data: String = "abc..."
}
let h1 = HTTP()
h1.run { (r: Result<[String], Error>) in } // 1 - Print "Base" => OK
let h2 = HTTP()
h2.run { (r: Result<Model, Error>) in } // 2 - Print "Base" => ???
HTTP.handle { (r: Result<[String], Error>) in } // 3 - Print "Base" => OK
HTTP.handle { (r: Result<Model, Error>) in } // 4 - Print "Offline" => OK
I am trying to figure out why in case 2, it prints "Base" instead of "Offline". If anyone has suggestions so the right handle method get called depending on the given type T.
I made the handle method static for demo/running purpose to show it works in a static context (case 3 and 4). As you can see, called from the HTTP instance context behaviour is different (case 2).
Any idea ?
It looks like the instance function run(handler:) because it sets as a constraint the T to be only Decodable, filters in some way the Offline constraint. So, passes a type that is only Decodable to the static function handle(handler:) and the compiler assumes that the callable function is the one with only Decodable as a constraint.I do not know if this is understandable.
You can have the expected behavior by adding an overload for your instance function in HTTP class:
class HTTP {
func run<T: Decodable>(handler: (Result<T, Error>) -> Void) {
HTTP.handle(handler: handler)
}
func run<T: Decodable & Offline>(handler: (Result<T, Error>) -> Void) {
HTTP.handle(handler: handler)
}
}
The correct HTTP.handle to call is decided at compile time, and baked into the binary. T is only promised to be Decodable in run. It might conform to other protocols, but that's all that's promised. So it compiles this into a call to the Decodable version, even if you call it with something that could be Offline.
This gets to the point of generic specializations. It's not intended to change behavior. It's intended generally to improve performance. For example, consider the following:
func f<Seq: Sequence>(_ seq: Seq) -> Bool { ... }
func f<Seq: RandomAccessCollection>(_ seq: Seq) -> Bool { ... }
Every RandomAccessCollection is a Sequence, so one or the other might be called for an Array. Both should always return the same result. But the second might be more efficient in cases where the system can prove that Seq is Array. If they returned different results, it's not going to work correctly.
Generics are not a way to reinvent class inheritance. If you really need class inheritance, then use classes and inheritance. But generally you should redesign your system to avoid that. How to do that depends on the real goal here.

How to express this `map` function using a protocol

I have the following code
enum AppSubscription<Event>: Subscription {
func map<ParentSubscription>(_ lift: #escaping (Event) -> (ParentSubscription.Event)) -> ParentSubscription where ParentSubscription: Subscription {
switch self {
case let .loadedArticles(cb):
return AppSubscription<ParentSubscription.Event>.loadedArticles { article, isHot in lift(cb(article, isHot)) } as! ParentSubscription
case let .savedArticles(cb):
return AppSubscription<ParentSubscription.Event>.savedArticles { article in lift(cb(article)) } as! ParentSubscription
case let .deletedArticles(cb):
return AppSubscription<ParentSubscription.Event>.deletedArticles { article in lift(cb(article)) } as! ParentSubscription
}
}
case loadedArticles((Article, Bool) -> Event)
case savedArticles((Article) -> Event)
case deletedArticles((Article) -> Event)
}
and I want to create a protocol for Subscription (that AppSubscription conforms to). This is my attempt so far
public protocol Subscription {
associatedtype Event
func map<ParentSubscription>(_ lift:
#escaping (Event) -> (ParentSubscription.Event)) -> ParentSubscription where
ParentSubscription: Subscription
}
but I get the errors
Subs.playground:21:31: Protocol 'Subscription' can only be used as a generic constraint because it has Self or associated type requirements
and
Subs.playground:21:28: Same-type requirement makes generic parameter 'ParentSubscription' non-generic
How do I write the protocol's map function to match the implementation in AppSubscription?
Edit: Switched to ParentSubscription: Subscription as per #Fabian's suggestion. Also added missing #escaping to AppSubscription.
With the changes I'm not sure how to reconcile the protocol with the implementation. If I use the signature exactly as written in the protocol
func map<ParentSubscription, ParentEvent>(_ lift: #escaping (Event) -> (ParentEvent)) -> ParentSubscription where ParentSubscription : Subscription, ParentEvent == ParentSubscription.Event {
then I'm forced to use as! ParentSubscription which seems redundant. If I use the signature
func map<ParentEvent>(_ lift: (Event) -> ParentEvent) -> AppSubscription<ParentEvent> {
then it complains that the AppSubscription does not conform to the protocol. Is the only way to get it to compile to use as! ?
Edit2: Removed redundant ParentEvent and replaced with ParentSubscription.Event as per another suggestion by #Fabian.
The code now compiles which is great and many thanks to #Fabian.
I do think it's fair to say that the amount of type hinting seems awkward, is there any way to avoid or abbreviate any of it?
Edit3: Adding a repl link https://repl.it/#opsb/ScratchyTrustworthyArchitect to make it easy to play with.

Why doesn't this behave the way I expect (hope) it would?

I have a several protocols set up in my framework to deal with resources. In one of the protocols, I have set up an extension to provide a default implementation for a decode function. It's simpler to show the code and what happens (see calls to fatalError). There's a lot more code in the actual implementation, but this illustrates the issue:
This is the "base" protocol:
public protocol Resourceful {
associatedtype AssociatedResource
typealias ResourceCompletionHandler = (AssociatedResource?, Error?) -> Void
func fetch(_ completion: #escaping ResourceCompletionHandler)
}
This is a generic, concrete implementaion of Resourceful:
open class WebResourceApiCall<Resource>: Resourceful {
public typealias AssociatedResource = Resource
public typealias FetchedResponse = (data: Data?, urlResponse: URLResponse?)
public init() {
}
public func fetch(_ completion: #escaping ResourceCompletionHandler) {
try! decode(fetched: (data: nil, urlResponse: nil))
}
public func decode(fetched: FetchedResponse) throws -> Resource {
fatalError("It ends up here, but I don't want it to!")
}
}
extension WebResourceApiCall where Resource: Decodable {
public func decode(fetched: FetchedResponse) throws -> Resource {
fatalError("This is where I want it to go...")
}
}
This is how I'm attempting to use it:
public struct Something: Decodable { }
var apiCall = WebResourceApiCall<Something>()
apiCall.fetch { _, _ in } // Implictly calls decode... but not the decode I expected it to! See fatalError() calls...
Instead of calling decode on the extension, like I hoped it would, the "default" decode method with no constraints is always called.
Why doesn't this work the way I expect it to?
Thanks in advance!
Swift is a statically dispatched language, thus the address of the decode() function to be called is computed at compile time, and because the call happens inside the base definition of the class, the compiler picks the original implementation.
Now, if you call the method from a place where the compiler has enough information to pick the implementation you need, it will work:
var apiCall = WebResourceApiCall<Something>()
try apiCall.decode(fetched: (nil, nil))
The above code will call the method from the specialized extension, as at this point the compiler is a better position to know that it has a more specialized implementation to call.
It should be possible to achieve the behaviour you need if you move the decode() method in the dynamic dispatch world - i.e. at the protocol level.

Protocol conformance of system classes using more generic types

To mock objects in Swift for test, I generally follow a pattern of authoring a Protocol describing the behaviour of the object I'd like, and then using Cuckoo to generate mocks for it for test.
Usually, these protocols map directly onto existing types, and this works fine, until I need to make the existing type work with my new protocol types.
public typealias RequestCompletionHandler = (Request, Error?) -> Swift.Void
public protocol Request {
var results: [Any]? { get }
var completionHandler: RequestCompletionHandler? { get }
}
extension VNRequest: Request {}
Here, VNRequest already has a member called completionHandler that returns the following type:
public typealias VNRequestCompletionHandler = (VNRequest, Error?) -> Swift.Void
Technically, all of these types should match up, but obviously it's not a very easy scenario for the type solver to solve, so the compiler isn't too cheerful about it.
At first I thought I'd be able to refer to the original completionBlock implementation by doing the following:
extension VNRequest: Request {
public var completionHandler: RequestCompletionHandler? {
return (self as VNRequest).completionHandler
}
}
But it's not too happy about that either.
Any advice about how best to do this? I've thought about using a different name in the protocol (e.g: completionBlock_ or completionBlock$), which works, but it's a bit scrappy.
The problem occurs due to the fact that Swift is covariant in respect to closure return type, and contra-variant in respect to its arguments. Which means that (VNRequest, Error?) -> Void can't be used where (Request, Error?) -> Void is needed (the other way around is possible).
You can solve your problem by using an associated type in the Request protocol:
public protocol Request {
associatedtype RequestType = Self
var results: [Any]? { get }
var completionHandler: ((RequestType, Error?) -> Void)? { get }
}
The above protocol definition will make the VNRequest class compile, as the compiler will detect the match for RequestType.
The downside, though, is that protocols with associated types have some limitations regarding where they can be used, and also passing them as function arguments will require some where clauses to make them work.
Another alternative would be to use Self as a parameter to the completion handler:
public typealias RequestCompletionHandler<T> = (T, Error?) -> Swift.Void
public protocol Request {
var results: [Any]? { get }
var completionHandler: RequestCompletionHandler<Self>? { get }
}
This will also solve the conformance issue, but also comes with some constraints: VNRequest must be final, the functions using Request must be generic:
func send<R: Request>(_ request: R)

Wrapping a callback type with Swift protocols

Let's say I'm dealing with a library I don't control and I'm trying to wrap a class that defines a callback type to decouple my code for testing purposes. Here's the class, inside module AXSwift:
public class Application {
public typealias Callback = (element: UIElement) -> ()
public func createObserver(callback: Callback) -> Observer? {
// ...
}
}
Here's the wrapper protocol for testing:
protocol UIElementProtocol {}
extension AXSwift.UIElement: UIElementProtocol {}
protocol ApplicationProtocol {
func createObserver(callback: (element: UIElementProtocol) -> ()) -> Observer?
}
extension AXSwift.Application: ApplicationProtocol {}
I get Type 'Application' does not conform to protocol 'ApplicationProtocol'. If I change UIElementProtocol inside the ApplicationProtocol callback back to UIElement, it works. But UIElement conforms to UIElementProtocol, so why doesn't this work?
Second question: Is there a better way to design the library API to allow for this sort of thing?
The solution was really simple, just implement the protocol in the extension with a method that calls the original method:
extension AXSwift.Application: ApplicationProtocol {
func createObserver(callback: (element: UIElementProtocol) -> ()) -> Observer? {
return createObserver(callback as Callback)
}
}
Where Callback is the expected type. Apparently you have to be explicit about passing a general function type as a more specific type.