How to make a generic class conform to a protocol for specific type? - swift

Say there exists a generic struct:
public struct Matrix<T> where T: FloatingPoint, T: ExpressibleByFloatLiteral {
// some methods...
}
Is it possible extend the struct to conform to a protocol for constrained T using where clauses? E.g. something like
extension Matrix where T: SpecificClass : SomeProtocol {
// This does not compile :(
}

No, such construct isn't possible (circa Swift 3.1 at least).
For instance:
class SomeClass { }
protocol SomeProtocol { }
extension Matrix: SomeProtocol where T == SomeClass { }
Gives a very clear error message:
Extension of type Matrix with constraints cannot have an inheritance clause.
But all is not lost... as correctly noted by Alexander, there is already a proposal lined up for Swift 4! The feature will be called Conditional Conformances (SE-0143).
A nice example for all protocol-oriented programming hackers out there:
extension Array: Equatable where Element: Equatable {
...
}
If an array contains equatable elements than said array is also equatable.
Update. Swift 4 is out but this feature hasn’t landed yet. We may need to wait until Swift 5 for this...

Related

How can I use a generic-type constraint extension for a class with a `Codable` type constraint?

Say my class definition is:
class Foo<T: Codable> {
let bar: T
}
I want to extend arrays of this type:
extension Array where Element: Foo<Codable> { /* do something based on `bar` values */ }
This produces the error
Protocol 'Codable' (aka 'Decodable & Encodable') as a type cannot conform to 'Decodable'
I did read in another question that conforming T to Encodable/Decodable isn't possible if T == Encodable/Decodable since a protocol can't conform to itself, but this is only for a certain property so I'm having trouble wrapping my head around it. Is there any way to achieve this?
I think is better to use protocol instead class like this.
protocol FooProtocol {
associatedtype T : Codable
var bar: T {get set}
}
extension Array where Element: FooProtocol {
}

Is it possible to extend structs conforming to a protocol?

I'm using SwiftCheck to do property based testing and I want to generate random elements for several enums conforming to the CaseIterable protocol. I thought that instead of writing the same code for all the enums I could do something like the following:
extension Any: Arbitrary where Self: CaseIterable {
public static var arbitrary: Gen<Self> {
return Gen<Self>.fromElements(of: Self.allCases)
}
}
This fails to compile for several reasons, the main one being
Error:(13, 1) non-nominal type 'Any' cannot be extended
Is there any way to express this in Swift?
You could simply extend CaseIterable itself if this computed property is supposed to exist on all types conforming to CaseIterable.
extension Arbitrary where Self: CaseIterable {
public static var arbitrary: Gen<Self> {
return Gen<Self>.fromElements(of: Array(Self.allCases))
}
}

Multiple constraints on parameter of a generic function

Reading about generic functions in Swift, I see that it is possible to put some constraints on a parameter by requiring, that it is a subclass of a given class C, or that it implements a given protocol P.
But I wonder if there is a way to require both at the same time. I haven't found anything about that yet.
Does anyone know?
Actually You can do that.
If you have seen Codable in swift it is actually Decodable and Encodable
typealias Codable = Decodable & Encodable
So in some function if you are using generic T as Codable
struct StructOfCodable<T:Codable>: Codable {
....
}
Here is example
protocol Test {}
class TClass {
}
typealias Common = Test & TClass
func generic <T:Common>(method:T) {
}
Another way is protocol and class both can have super class. So you can create common protocol
like
protocol CommonInProtocolAndStruct { }
protocol ProtocolUsedAsConstraint:CommonInProtocolAndStruct {}
struct StructUsedAsConstraint:CommonInProtocolAndStruct {}
And any method you can use CommonInProtocolAndStruct as generic constraint
You can add any number of type constraints using a where clause. Examples:
import UIKit
func f<T>(t: T) where T: UIView, T: Encodable {}
class C<T> where T: UIView, T: Encodable {}

Using some protocol as a concrete type conforming to another protocol is not supported

I’m trying to mix generics with protocols and I’m getting a really hard time xD
I have certain architecture implemented in an Android/Java project, and I’m trying to rewrite it to fit it in a swift/iOS project. But I’ve found this limitation.
ProtocolA
protocol ProtocolA {
}
ProtocolB
protocol ProtocolB : ProtocolA {
}
ImplementProtocolA
class ImplementProtocolA <P : ProtocolA> {
let currentProtocol : P
init(currentProtocol : P) {
self.currentProtocol = currentProtocol
}
}
ImplementProtocolB
class ImplementProtocolB : ImplementProtocolA<ProtocolB> {
}
So, when I try to set ProtocolB as the concrete type that implements ProtocolA, I get this error:
Using 'ProtocolB' as a concrete type conforming to protocol 'ProtocolA' is not supported
1 Is there any reason for this “limitation”?
2 Is there any workaround to get this implemented?
3 Will it be supported at some point?
--UPDATED--
Another variant of the same problem, I think:
View protocols
protocol View {
}
protocol GetUserView : View {
func showProgress()
func hideProgress()
func showError(message:String)
func showUser(userDemo:UserDemo)
}
Presenter protocols
protocol Presenter {
typealias V : View
}
class UserDemoPresenter : Presenter {
typealias V = GetUserView
}
Error:
UserDemoPresenter.swift Possibly intended match 'V' (aka
'GetUserView') does not conform to 'View’
What is that?? It conforms!
Even if I use View instead of GetUserView, it does not compile.
class UserDemoPresenter : Presenter {
typealias V = View
}
UserDemoPresenter.swift Possibly intended match 'V' (aka 'View') does
not conform to 'View'
xxDD I don’t get it, really.
--UPDATED--
With the solution proposed by Rob Napier the problem is not fixed, instead, it is just delayed.
When a try to define a reference to UserDemoPresenter, I need to specify the generic type, so I get the same error:
private var presenter : UserDemoPresenter<GetUserView>
Using 'GetUserView' as a concrete type conforming to protocol
'GetUserView' is not supported
The underlying reason for the limitation is that Swift doesn't have first-class metatypes. The simplest example is that this doesn't work:
func isEmpty(xs: Array) -> Bool {
return xs.count == 0
}
In theory, this code could work, and if it did there would be a lot of other types I could make (like Functor and Monad, which really can't be expressed in Swift today). But you can't. You need to help Swift nail this down to a concrete type. Often we do that with generics:
func isEmpty<T>(xs: [T]) -> Bool {
return xs.count == 0
}
Notice that T is totally redundant here. There is no reason I should have to express it; it's never used. But Swift requires it so it can turn the abstract Array into the concrete [T]. The same is true in your case.
This is a concrete type (well, it's an abstract type that will be turned into a concrete type any time it's instantiated and P is filled in):
class ImplementProtocolA<P : ProtocolA>
This is a fully abstract type that Swift doesn't have any rule to turn into a concrete type:
class ImplementProtocolB : ImplementProtocolA<ProtocolB>
You need to make it concrete. This will compile:
class ImplementProtocolB<T: ProtocolB> : ImplementProtocolA<T> {}
And also:
class UserDemoPresenter<T: GetUserView> : Presenter {
typealias V = T
}
Just because you're likely to run into the issue later: your life will go much easier if you'll make these structs or final classes. Mixing protocols, generics, and class polymorphism is full of very sharp edges. Sometimes you're lucky and it just won't compile. Sometimes it will call things you don't expect.
You may be interested in A Little Respect for AnySequence which details some related issues.
private var presenter : UserDemoPresenter<GetUserView>
This is still an abstract type. You mean:
final class Something<T: GetUserView> {
private var presenter: UserDemoPresenter<T>
}
If that creates a problem, you'll need to create a box. See Protocol doesn't conform to itself? for discussion of how you type-erase so that you can hold abstract types. But you need to work in concrete types. You can't ultimately specialize on a protocol. You must eventually specialize on something concrete in the majority of cases.

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.