Swift protocols and equatable - swift

I'm still learning how to work with arrays of objects implementing protocols with associated types.
I have the following protocols:
public protocol Word : Equatable, Hashable { // compiles
associatedtype WordType : Equatable
var moreWords: [WordType] { get }
}
public protocol WordDataSource { // compiles
associatedtype SomeWord : Word
func findWord(spelling: String) -> SomeWord?
}
I have WordA, WordB and WordC all implementing Word and subclassing NSObject
Basically, I want to implement the datasource protocol using different kinds of class implementing the Word class. This is the kind of code I would like to write, but obviously it doesn't compile.
class MyDataSource : WordDataSource {
func findWord(spelling: String) -> SomeWord? {
if conditionA {
return WordA()
}
if conditionB {
return WordB()
}
if conditionA {
return WordC()
}
}
}
Is that even possible in Swift? What should I write to make that work?
Thanks a lot for your help!

This is not possible, and it's not possible for a reason. Let's assume that your class MyDataSource does compile. Now, we could write such code:
let fooWord = MyDataSource().findWord(spelling: "Foo") // Would return WordA instance
let barWord = MyDataSource().findWord(spelling: "Bar") // Would return WordB instance
but all we know about those two types is that they are of this SomeWord type. So they should be comparable, since Word is comparable, right?
But they're two completely different types, so how would you know how should they be compared? Take a look at the definition of Equatable protocol:
public static func ==(lhs: Self, rhs: Self) -> Bool
You can only compare two objects of the same type that conform to this protocol.

Related

Swift - Compare generics when equatable

I need to compare a generic parameter when it's comparable, something like this:
class MyClass<Item> {
var items: [[Items]]? {
didSet {
if Item is Equatable {
print(oldValue == items)
} else {
print(false)
}
}
}
}
How can I achieve this?
I've also tried:
extension TableViewModel where Item: Equatable {
func f() -> Bool {
return true
}
}
extension TableViewModel {
func f() -> Bool {
return false
}
}
and adding an f() call to my didSet.
just to see if I can get the f() function from the Equatable extension to get called (had no success).
Without beeing able to see your classes etc, i was able to produce what you want. You need to make sure the equatable methods are used properly however.
class Item {
}
class Items {
}
class Item2 : Item, Equatable {
static func ==(_ lhs: Item2,_ rhs: Item2) -> Bool{
return true
}
static func !=(_ lhs: Item2,_ rhs: Item2) -> Bool{
return true
}
}
class MyClass<Item> {
var items: [[Items]]? {
didSet {
}
}
}
extension MyClass where Item : Equatable {
func f() {
print("Hello")
}
}
class TheClass : MyClass<Item2> {
func someFunc() {
super.f()
}
}
let it = TheClass()
it.f() //prints : "Hello"
You just had to go an extra sub-classing step, by applying Equatable to the subclasses of Item (for those you wanted subclassed in the first place).
Explanation:
you wanted a class which if subclassed for a generic Equatable type, could perform the method f(). This being said, you delegated Item as your top level type for conditionnally checking Equatable. That means since Item can or cannot be Equatable, you can only apply the protocol to a subclass of Item. If you applied Equatable straight to Item, you wouldn't have your problem. So to demonstrate this, i subclassed Item into Item2, and added Equatable to Item2. Thus creating a possibility for Item to exist in Equatable form.
Now you bring in MyClass<Item>, which is a seperate entity altogether, but which can adopt a type of class which inherits from or is Item altogether. But, since you specify that you only want subclasses of Item which are Equatable (ie: class Item2), you need to extend MyClass conditionaly for the types of Item which are Equatable as well. Hence the extension.
So finally, to demonstrate the whole subclassing logic, you need to subclass MyClass<Item>.. AGAIN.. but this time, by specifying this subclass to take Item2 as a generic type in order to explicitely declare that this subclass is equatable, because the only sub-generic Item type which is equatable is Item2 in your case.
Just by re-reading myself, it can be confusing.. But what you need to remember is that by applying generics, and by conforming them to protocols by CONDITIONAL EXTENSION, you allow for several different types of that same generic class to exist. Therefore, it increases the number of subclassing needed in order to adopt certain methods, in specific use-cases.
Also. As for your items : [[Item]]? didSet{} variable. I'm looking into it.. Howver i've found in this post a way to make it work with Equatable in Swift 4. I'm updating to Swift 4 as we speak to test out my code.

Using diff in an array of objects that conform to a protocol

I'm experimenting with using Composition instead of Inheritance and I wanted to use diff on an array of objects that comply with a given protocol.
To do so, I implemented a protocol and made it comply with Equatable:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field:Equatable {
var content: String { get }
}
func ==<T: Field>(lhs: T, rhs: T) -> Bool {
return lhs.content == rhs.content
}
func ==<T: Field, U: Field>(lhs: T, rhs: U) -> Bool {
return lhs.content == rhs.content
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")] // đź’Ą boom
But I've soon discovered that:
error: protocol 'Field' can only be used as a generic constraint because it has Self or associated type requirements
I understand why since Swift is a type-safe language that needs to be able to know the concrete type of these objects at anytime.
After tinkering around, I ended up removing Equatable from the protocol and overloading the == operator:
// Playground - noun: a place where people can play
import XCPlayground
import Foundation
protocol Field {
var content: String { get }
}
func ==(lhs: Field, rhs: Field) -> Bool {
return lhs.content == rhs.content
}
func ==(lhs: [Field], rhs: [Field]) -> Bool {
return (lhs.count == rhs.count) && (zip(lhs, rhs).map(==).reduce(true, { $0 && $1 })) // naive, but let's go with it for the sake of the argument
}
struct First:Field {
let content:String
}
struct Second:Field {
let content:String
}
// Requirement #1: direct object comparison
print(First(content: "abc") == First(content: "abc")) // true
print(First(content: "abc") == Second(content: "abc")) // false
// Requirement #2: being able to diff an array of objects complying with the Field protocol
let array1:[Field] = [First(content: "abc"), Second(content: "abc")]
let array2:[Field] = [Second(content: "abc")]
print(array1 == array2) // false
let outcome = array1.diff(array2) // đź’Ą boom
error: value of type '[Field]' has no member 'diff'
From here on, I'm a bit lost to be honest. I read some great posts about type erasure but even the provided examples suffered from the same issue (which I assume is the lack of conformance to Equatable).
Am I right? And if so, how can this be done?
UPDATE:
I had to stop this experiment for a while and totally forgot about a dependency, sorry! Diff is a method provided by SwiftLCS, an implementation of the longest common subsequence (LCS) algorithm.
TL;DR:
The Field protocol needs to comply with Equatable but so far I have not been able to do this. I need to be able to create an array of objects that comply to this protocol (see the error in the first code block).
Thanks again
The problem comes from a combination of the meaning of the Equatable protocol and Swift’s support for type overloaded functions.
Let’s take a look at the Equatable protocol:
protocol Equatable
{
static func ==(Self, Self) -> Bool
}
What does this mean? Well it’s important to understand what “equatable” actually means in the context of Swift. “Equatable” is a trait of a structure or class that make it so that any instance of that structure or class can be compared for equality with any other instance of that structure or class. It says nothing about comparing it for equality with an instance of a different class or structure.
Think about it. Int and String are both types that are Equatable. 13 == 13 and "meredith" == "meredith". But does 13 == "meredith"?
The Equatable protocol only cares about when both things to be compared are of the same type. It says nothing about what happens when the two things are of different types. That’s why both arguments in the definition of ==(::) are of type Self.
Let’s look at what happened in your example.
protocol Field:Equatable
{
var content:String { get }
}
func ==<T:Field>(lhs:T, rhs:T) -> Bool
{
return lhs.content == rhs.content
}
func ==<T:Field, U:Field>(lhs:T, rhs:U) -> Bool
{
return lhs.content == rhs.content
}
You provided two overloads for the == operator. But only the first one has to do with Equatable conformance. The second overload is the one that gets applied when you do
First(content: "abc") == Second(content: "abc")
which has nothing to do with the Equatable protocol.
Here’s a point of confusion. Equability across instances of the same type is a lower requirement than equability across instances of different types when we’re talking about individually bound instances of types you want to test for equality. (Since we can assume both things being tested are of the same type.)
However, when we make an array of things that conform to Equatable, this is a higher requirement than making an array of things that can be tested for equality, since what you are saying is that every item in the array can be compared as if they were both of the same type. But since your structs are of different types, you can’t guarantee this, and so the code fails to compile.
Here’s another way to think of it.
Protocols without associated type requirements, and protocols with associated type requirements are really two different animals. Protocols without Self basically look and behave like types. Protocols with Self are traits that types themselves conform to. In essence, they go “up a level”, like a type of type. (Related in concept to metatypes.)
That’s why it makes no sense to write something like this:
let array:[Equatable] = [5, "a", false]
You can write this:
let array:[Int] = [5, 6, 7]
or this:
let array:[String] = ["a", "b", "c"]
or this:
let array:[Bool] = [false, true, false]
Because Int, String, and Bool are types. Equatable isn’t a type, it’s a type of a type.
It would make “sense” to write something like this…
let array:[Equatable] = [Int.self, String.self, Bool.self]
though this is really stretching the bounds of type-safe programming and so Swift doesn’t allow this. You’d need a fully flexible metatyping system like Python’s to express an idea like that.
So how do we solve your problem? Well, first of all realize that the only reason it makes sense to apply SwiftLCS on your array is because, at some level, all of your array elements can be reduced to an array of keys that are all of the same Equatable type. In this case, it’s String, since you can get an array keys:[String] by doing [Field](...).map{ $0.content }. Perhaps if we redesigned SwiftLCS, this would make a better interface for it.
However, since we can only compare our array of Fields directly, we need to make sure they can all be upcast to the same type, and the way to do that is with inheritance.
class Field:Equatable
{
let content:String
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.content == rhs.content
}
init(_ content:String)
{
self.content = content
}
}
class First:Field
{
init(content:String)
{
super.init(content)
}
}
class Second:Field
{
init(content:String)
{
super.init(content)
}
}
let items:[Field] = [First(content: "abc"), Second(content: "cxz")]
The array then upcasts them all to type Field which is Equatable.
By the way, ironically, the “protocol-oriented” solution to this problem actually still involves inheritance. The SwiftLCS API would provide a protocol like
protocol LCSElement
{
associatedtype Key:Equatable
var key:Key { get }
}
We would specialize it with a superclass
class Field:LCSElement
{
let key:String // <- this is what specializes Key to a concrete type
static func == (lhs:Field, rhs:Field) -> Bool
{
return lhs.key == rhs.key
}
init(_ key:String)
{
self.key = key
}
}
and the library would use it as
func LCS<T: LCSElement>(array:[T])
{
array[0].key == array[1].key
...
}
Protocols and Inheritance are not opposites or substitutes for one another. They complement each other.
I know this is probably now what you want but the only way I know how to make it work is to introduce additional wrapper class:
struct FieldEquatableWrapper: Equatable {
let wrapped: Field
public static func ==(lhs: FieldEquatableWrapper, rhs: FieldEquatableWrapper) -> Bool {
return lhs.wrapped.content == rhs.wrapped.content
}
public static func diff(_ coll: [Field], _ otherCollection: [Field]) -> Diff<Int> {
let w1 = coll.map({ FieldEquatableWrapper(wrapped: $0) })
let w2 = otherCollection.map({ FieldEquatableWrapper(wrapped: $0) })
return w1.diff(w2)
}
}
and then you can do
let outcome = FieldEquatableWrapper.diff(array1, array2)
I don't think you can make Field to conform to Equatable at all as it is designed to be "type-safe" using Self pseudo-class. And this is one reason for the wrapper class. Unfortunately there seems to be one more issue that I don't know how to fix: I can't put this "wrapped" diff into Collection or Array extension and still make it support heterogenous [Field] array without compilation error:
using 'Field' as a concrete type conforming to protocol 'Field' is not supported
If anyone knows a better solution, I'm interested as well.
P.S.
In the question you mention that
print(First(content: "abc") == Second(content: "abc")) // false
but I expect that to be true given the way you defined your == operator

How can I call a static function on a protocol in a generic way?

Is there a point to declaring a static function on a protocol? The client using the protocol has to call the function on a type conforming to the protocol anyway right? That breaks the idea of not having to know the type conforming to the protocol IMO. Is there a way to call the static function on the protocol in a way where I don't have to know the actual type conforming to my protocol?
Nice question. Here is my humble point of view:
Is there a point to declaring a static function on a protocol?
Pretty much the same as having instance methods declared in a protocol.
The client using the protocol has to call the function on a type conforming to the protocol anyway right?
Yes, exactly like instance functions.
That breaks the idea of not having to know the type conforming to the protocol IMO.
Nope. Look at the following code:
protocol Feline {
var name: String { get }
static func createRandomFeline() -> Feline
init()
}
extension Feline {
static func createRandomFeline() -> Feline {
return arc4random_uniform(2) > 0 ? Tiger() : Leopard()
}
}
class Tiger: Feline {
let name = "Tiger"
required init() {}
}
class Leopard: Feline {
let name = "Leopard"
required init() {}
}
let feline: Feline = arc4random_uniform(2) > 0 ? Tiger() : Leopard()
let anotherFeline = feline.dynamicType.createRandomFeline()
I don't know the real type inside the variable feline. I just know that it does conform to Feline. However I am invoking a static protocol method.
Is there a better way to do this?
I see, you would like to call a static method/function declared in a protocol without creating a value that conforms to the protocol.
Something like this:
Feline.createRandomFeline() // DANGER: compiler is not happy now
Honestly I don't know the reason why this is not possible.
yes this is possible:
Swift 3
protocol Thing {
static func genericFunction()
}
//... in another file
var things:[Thing] = []
for thing in things {
type(of: thing).genericFunction()
}
Thank you #appzYourLife for the help! Your answer inspired my answer.
#appzYourLife answered my question. I had an underlying issue I was trying to resolve and the following code resolves my issue, so I'll post this here, maybe it helps someone with my same underlying question:
protocol MyProtocol {
static func aStaticFunc()
}
class SomeClassThatUsesMyProtocolButDoesntConformToIt {
var myProtocolType: MyProtocol.Type
init(protocolType: MyProtocol.Type) {
myProtocolType = protocolType
}
func aFunction() {
myProtocolType.aStaticFunc()
}
}
I created another solution for this case. IMHO this is quite clean and simple.
First, create a protocol for accessing instance type.
protocol TypeAccessible {
func type() -> Self.Type
}
extension TypeAccessible {
func type() -> Self.Type {
return Swift.type(of: self)
}
}
then create your concrete class as here. The point is your protocol should conform to TypeAccessible protocol.
protocol FooProtocol: TypeAccessible {
static func bar()
}
class Foo: FooProtocol {
static func bar() { }
}
On call site use it as
let instance: FooProtocol = Foo()
instance.type().bar()
For further use cases, just make sure your protocols conform to TypeAccessible and that's all.
A little late to the party on this one.
Here's my solution for "adding" static properties/functions/types to a protocol using typealias.
For example:
enum PropertyScope {
case all
case none
}
struct PropertyNotifications {
static var propertyDidChange =
Notification.Name("propertyDidChangeNotification")
}
protocol Property {
typealias Scope = PropertyScope
typealias Notifications = PropertyNotifications
var scope: Scope { get set }
}
Then you can do this anywhere in your code:
func postNotification() {
let scope: Property.Scope = .all
NotificationCenter.post(name: Property.Notifications.propertyDidChange,
object: scope)
}
Using protocols like Java interfaces is rarely a good idea. They are meta types, meant for defining contracts, which is an entirely different kind of thing.
That being said, just for the point of understanding, I find the most simple and effective way for creating the equivalent of a static factory method of a protocol to write a free function.
It should contain the protocol's name, hoping that that will prevent name clashes, and improve discoverability.
In other languages, createP would be a static member of P, named create and be called as P.create(...), which would drastically improve discoverability and guarantee to prevent name clashes.
In swift, though, this is not an option for protocols, so if protocols are for some reason really actually used as a replacement for interfaces, at least including the protocol's name in the function's name is an ugly workaround that's still slightly better than nothing.
P.S. in case the goal is actually to achieve something like an inheritance hierarchy with structs, union style enums are the tool that's meant to serve that purpose :)
protocol P
{
var x: Int { get }
}
func createP() -> P
{
if (todayIsMonday())
{
return A()
}
else
{
return B()
}
}
class A: P
{
var x = 5
}
class B: P
{
var x = 7
}
This isn't an answer so much as it is an extension to the question. Say I have:
#objc public protocol InteractivelyNameable: Nameable {
static func alertViewForNaming(completion:#escaping((_ success: Bool, _ didCancel: Bool, _ error: Error?) -> Void)) -> UIAlertController?
}
And I have a generic view controller that manages various types (generic type is .fetchableObjectType... basically NSFetchResult). I need to check if a specific object type conforms to the protocol, and if so, invoke it.
something like:
// valid swift code
if self.dataSource.fetchableObjectType is InteractivelyNameable {
// not valid swift code
if let alert = (self.dataSource.fetchableObjectType as InteractivelyNameable).alertViewForNaming(....)
}
I had a situation where I need to create same DomainModel object from 2 different response. so this (static method in protocol helped me) approach helped me.
protocol BaseResponseKeyList: CodingKey {
static func getNameKey()->Self
}
enum FirstResponseKeyList: String, BaseResponseKeyList {
case name
func getNameKey()->FirstResponseKeyList {
return .name
}
}
enum SecondResponseKeyList: String, BaseResponseKeyList {
case userName
func getNameKey()->SecondResponseKeyList {
return .userName
}
}
struct MyDomainModel<T:BaseResponseKeyList> : Decodable {
var name:String?
required init(from d:Decoder) {
do {
let container = try d.container(keyedBy:T.self)
name = try container.decode(String.self, forKey:T.getNameKey())
}catch(_) {
print("error")
}
}
}
let myDomainModel = try JSONDecoder().decode(MyDomainModel <FirstResponseKeyList>.self, from: data)
let myDomainModel2 = try JSONDecoder().decode(MyDomainModel <SecondResponseKeyList>.self, from: data2)

Swift: Generic Protocols

I have some swift structs for which protocol compliance is generated with individual extensions with equal methods names which just differ in their return types which are struct dependent. On top of That I want to use them in a generic function which Calls a protocol conforming function for a generic type).
I tried to accomplish this like that:
//: Playground - noun: a place where people can play
import UIKit
protocol FooProt {
typealias T;
static func createMe<T>()->T;
}
struct FooStruct{
}
extension FooStruct: FooProt{
typealias T = FooStruct;
static func createMe () -> FooStruct{
return FooStruct();
}
}
class Creator{
fun createOne<T where T:FooProt>(type:T.Type){
let instance = T.createMe();
}
}
Unfortunately I get the following error :
/var/folders/sn/78_zvfd15d74dzn01mdv258h0000gq/T/./lldb/3741/playground6.swift:7 :17: note: protocol requires function 'createMe()' with type ' () -> T' (aka '<Ď„_1_0> () -> Ď„_1_0')
static func createMe()->T;
What exactly doesn't comply here and is there a workaround ?
There are several problems with your code. On the one hand you have defined a protocol with an associated type. However, you define your createMe() method as a generic which uses some other type. I don't think that was your intent. I think your intent was to have a createMe() method that returns the same type as the protocol's associated type. In this case you need to remove the from the createMe() method. Also, the name createMe() implies that you aren't just returning any type, but the type of the object on which this method is being called. In this case, you don't even need an associated type protocol. You just need a protocol with a Self constraint which allows your code to be a bit simpler. In your Creator's createOne method, your type constraint is more complex than needed.
I think you want the following code:
protocol FooProt {
static func createMe()->Self;
}
struct FooStruct{
}
extension FooStruct: FooProt {
static func createMe() -> FooStruct {
return FooStruct();
}
}
class Creator{
func createOne<T:FooProt>(type: T.Type) -> T {
return T.createMe()
}
}
let foo = Creator().createOne(FooStruct.self)
Here is an alternate solution using an initializer in the protocol instead of a static method.
protocol FooProt {
init()
}
struct FooStruct{
}
extension FooStruct: FooProt {
}
class Creator{
func createOne<T:FooProt>(type: T.Type) -> T {
return T.init()
}
}
let foo = Creator().createOne(FooStruct.self)

Define a Swift protocol which requires a specific type of sequence

Suppose for example we're talking about elements of type Int (but the question still applies to any type)
I have some functionality which needs to loop over a sequence of Ints. But I don't care if behind the scenes this sequence is implemented as an Array, or a Set or any other exotic kind of structure, the only requirement is that we can loop over them.
Swift standard library defines the protocol SequenceType as "A type that can be iterated with a for...in loop". So my instinct is to define a protocol like this:
protocol HasSequenceOfInts {
var seq : SequenceType<Int> { get }
}
But this doesn't work. SequenceType is not a generic type which can be specialized, it's a protocol. Any particular SequenceType does have a specific type of element, but it's only available as an associated type: SequenceType.Generator.Element
So the question is:
How can we define a protocol which requires a specific type of sequence?
Here's some other things I've tried and why they aren't right:
Fail 1
protocol HasSequenceOfInts {
var seq : SequenceType { get }
}
Protocol 'SequenceType' can only be used as a generic constraint
because it has Self or associated type requirements
Fail 2
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
class ArrayOfInts : HasSequenceOfInts {
var seq : [Int] = [0,1,2]
}
I thought this one would work, but when I tried a concrete implementation using an Array we get
Type 'ArrayOfInts' does not conform to protocol 'HasSequenceOfInts'
This is because Array is not AnySequence (to my surprise... my expectation was that AnySequence would match any sequence of Ints)
Fail 3
protocol HasSequenceOfInts {
typealias S : SequenceType
var seq : S { get }
}
Compiles, but there's no obligation that the elements of the sequence seq have type Int
Fail 4
protocol HasSequenceOfInts {
var seq : SequenceType where S.Generator.Element == Int
}
Can't use a where clause there
So now I'm totally out of ideas. I can easily just make my protocol require an Array of Int, but then I'm restricting the implementation for no good reason, and that feels very un-swift.
Update Success
See answer from #rob-napier which explains things very well. My Fail 2 was pretty close. Using AnySequence can work, but in your conforming class you need to make sure you convert from whatever kind of sequence you're using to AnySequence. For example:
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
class ArrayOfInts : HasSequenceOfInts {
var _seq : [Int] = [0,1,2]
var seq : AnySequence<Int> {
get {
return AnySequence(self._seq)
}
}
}
There are two sides to this problem:
Accepting an arbitrary sequence of ints
Returning or storing an arbitrary sequence of ints
In the first case, the answer is to use generics. For example:
func iterateOverInts<SeqInt: SequenceType where SeqInt.Generator.Element == Int>(xs: SeqInt) {
for x in xs {
print(x)
}
}
In the second case, you need a type-eraser. A type-eraser is a wrapper that hides the actual type of some underlying implementation and presents only the interface. Swift has several of them in stdlib, mostly prefixed with the word Any. In this case you want AnySequence.
func doubles(xs: [Int]) -> AnySequence<Int> {
return AnySequence( xs.lazy.map { $0 * 2 } )
}
For more on AnySequence and type-erasers in general, see A Little Respect for AnySequence.
If you need it in protocol form (usually you don't; you just need to use a generic as in iterateOverInts), the type eraser is also the tool there:
protocol HasSequenceOfInts {
var seq : AnySequence<Int> { get }
}
But seq must return AnySequence<Int>. It can't return [Int].
There is one more layer deeper you can take this, but sometimes it creates more trouble than it solves. You could define:
protocol HasSequenceOfInts {
typealias SeqInt : IntegerType
var seq: SeqInt { get }
}
But now HasSequenceOfInts has a typealias with all the limitations that implies. SeqInt could be any kind of IntegerType (not just Int), so looks just like a constrained SequenceType, and will generally need its own type eraser. So occasionally this technique is useful, but in your specific case it just gets you basically back where you started. You can't constrain SeqInt to Int here. It has to be to a protocol (of course you could invent a protocol and make Int the only conforming type, but that doesn't change much).
BTW, regarding type-erasers, as you can probably see they're very mechanical. They're just a box that forwards to something else. That suggests that in the future the compiler will be able to auto-generate these type-erasers for us. The compiler has fixed other boxing problems for us over time. For instance, you used to have to create a Box class to hold enums that had generic associated values. Now that's done semi-automatically with indirect. We could imagine a similar mechanism being added to automatically create AnySequence when it's required by the compiler. So I don't think this is a deep "Swift's design doesn't allow it." I think it's just "the Swift compiler doesn't handle it yet."
(Tested and working in Swift 4, which introduces the associatedtype constraints needed for this)
Declare your original protocol that things will conform to:
protocol HasSequenceOfInts {
associatedType IntSequence : Sequence where IntSequence.Element == Int
var seq : IntSequence { get }
}
Now, you can just write
class ArrayOfInts : HasSequenceOfInts {
var seq : [Int] = [0,1,2]
}
like you always wanted.
However, if you try to make an array of type [HasSequenceOfInts], or assign it to a variable (or basically do anything with it), you'll get an error that says
Protocol 'HasSequenceOfInts' can only be used as a generic constraint because it has Self or associated type requirements
Now comes the fun part.
We will create another protocol HasSequenceOfInts_ (feel free to choose a more descriptive name) which will not have associated type requirements, and will automatically be conformed to by HasSequenceOfInts:
protocol HasSequenceOfInts: HasSequenceOfInts_ {
associatedType IntSequence : Sequence where IntSequence.Element == Int
var seq : IntSequence { get }
}
protocol HasSequenceOfInts_ {
var seq : AnySequence<Int> { get }
}
extension HasSequenceOfInts_ where Self : HasSequenceOfInts {
var seq_ : AnySequence<Int> {
return AnySequence(seq)
}
}
Note that you never need to need to explicitly conform to HasSequenceOfInts_ , because HasSequenceOfInts already conforms to it, and you get a full implementation for free from the extension.
Now, if you need to make an array or assign an instance of something conforming to this protocol to a variable, use HasSequenceOfInts_ as the type instead of HasSequenceOfInts, and access the seq_ property (note: since function overloading is allowed, if you made a function seq() instead of an instance variable, you could give it the same name and it would work):
let a: HasSequenceOfInts_ = ArrayOfInts()
a.seq_
This needs a bit more setup than the accepted answer, but means you don't have to remember to wrap your return value in AnySequence(...) in every type where you implement the protocol.
I believe you need to drop the requirement for it to only be Int's and work around it with generics:
protocol HasSequence {
typealias S : SequenceType
var seq : S { get }
}
struct A : HasSequence {
var seq = [1, 2, 3]
}
struct B : HasSequence {
var seq : Set<String> = ["a", "b", "c"]
}
func printSum<T : HasSequence where T.S.Generator.Element == Int>(t : T) {
print(t.seq.reduce(0, combine: +))
}
printSum(A())
printSum(B()) // Error: B.S.Generator.Element != Int
In Swift's current state, you can't do exactly what you want, maybe in the future though.
it is very specific example on request of Daniel Howard
1) type conforming to SequenceType protocol could be almost any sequence, even though Array or Set are both conforming to SequenceType protocol, most of their functionality comes from inheritance on CollectionType (which conforms to SequenceType)
Daniel, try this simple example in your Playground
import Foundation
public struct RandomIntGenerator: GeneratorType, SequenceType {
public func next() -> Int? {
return random()
}
public func nextValue() -> Int {
return next()!
}
public func generate() -> RandomIntGenerator {
return self
}
}
let rs = RandomIntGenerator()
for r in rs {
print(r)
}
As you can see, it conforms to SequenceType protocol and produce infinite stream of Int numbers. Before you will try to implement something, you have to answer yourself few questions
can i reuse some functionality, which is available 'for free' in standard Swift library?
am i trying to mimic some functionality which is not supported by Swift? Swift is not C++, Swift is not ObjectiveC ... and lot of constructions we used to use before Swift has no equivalent in Swift.
Define your question in such way that we can understand you requirements
are you looking for something like this?
protocol P {
typealias Type: SequenceType
var value: Type { get set }
}
extension P {
func foo() {
for v in value {
dump(v)
}
}
}
struct S<T: CollectionType>: P {
typealias Type = T
var value: Type
}
var s = S(value: [Int]())
s.value.append(1)
s.value.append(2)
s.foo()
/*
- 1
- 2
*/
let set: Set<String> = ["alfa", "beta", "gama"]
let s2 = S(value: set)
s2.foo()
/*
- beta
- alfa
- gama
*/
// !!!! WARNING !!!
// this is NOT possible
s = s2
// error: cannot assign value of type 'S<Set<String>>' to type 'S<[Int]>' (aka 'S<Array<Int>>')