Is it possible to extend structs conforming to a protocol? - swift

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))
}
}

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 {
}

Problem writing a generic function that accepts any value that is RawRepresentable by a CustomStringConvertible

I'm trying to write a function that accepts any value that is RawRepresentable by a CustomStringConvertible. I tried writing this:
enum MyEnum: String {
case a = "someString"
}
func myFunction<R: RawRepresentable>(val: R) where R.RawValue == CustomStringConvertible {
print(val.rawValue.description)
}
myFunction(val: MyEnum.a)
However I get the following error:
Global function 'myFunction(val:)' requires the types 'String' and 'CustomStringConvertible' be equivalent
Which is weird, since String does conform to CustomStringConvertible.
Conforming the RawValue to just String works, however, I'd like to make this work with other CustomStringConvertible.
Why does this not compile, and is there a way I can achieve that?
You should say that it conforms the protocol
where R.RawValue: CustomStringConvertible
Now it works also for other types
enum MyEnum2: Int {
case one = 1
}
myFunction(val: MyEnum2.one)

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

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...

How to list Swift types that conform to protocol using reflection?

How does one list types that conform to a protocol for purely Swift based types (i.e. no #objc annotation involved)? I was hoping that a reflection API that allows for this would be provided in the Swift standard library.
Just to be doubly sure, I am aware of the more specialised case of Objective-C or #objc annotated Swift classes conforming to a protocol solvable with Objective-C runtime APIs: How to list all classes conforming to protocol in Swift? – what I am after is the same for an arbitrary Swift type that may be a struct, enum or a class.
Here is my feeble failed attempt at using the Mirror API for the purpose:
protocol Derpable {
func derp();
}
extension Derpable {
func derp() {
print("Herp derp.")
}
}
enum E: Derpable { }
class C: Derpable { }
struct S: Derpable { }
print(Mirror(reflecting: Derpable.self).children.count) // prints "0\n"

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.