Define a protocol that holds Collection as a property - swift

I want to define a protocol that defines a variable "sequence" that is of type "Collection". I want that, because i have several conforming types, that will all hold an array of different types. Example:
protocol BioSequence {
associatedtype T
var sequence: Collection { get }
// Will also implement conformance to Collection and redirect all Collection functions to the property "sequence". This is because my types that conform to BioSequence are just wrappers for an array of Nucleotide or AminoAcid with some extra functionality
}
struct DNASequence: BioSequence {
// Will hold "sequence" of type [Nucleotide]
}
struct AminoSequence: BioSequence {
// Will hold "sequence" of type [AminoAcid]
}
Why do i want this? Because i need to implement the conformance to "Collection" only once in BioSequence and all conforming type inherit it automatically. Plus i can freely add extra functionality on the conforming types.
Now, when i try this like the code above, the compiler says: "Protocol Collection can only be used as a generic constraint". Yes, i googled what this error means, but how can i actually fix it to make my code work, like i want. Or is it not even possible to do what i want?
Thank you.

You can easily achieve this by using an associatedtype in your protocol, which can be constrained to Collection, allowing conforming types to satisfy the requirement with a concrete type when they adopt the protocol.
For example:
protocol CollectionWrapper : Collection {
associatedtype Base : Collection
var base: Base { get }
}
extension CollectionWrapper {
var startIndex: Base.Index {
return base.startIndex
}
var endIndex: Base.Index {
return base.endIndex
}
func index(after i: Base.Index) -> Base.Index {
return base.index(after: i)
}
subscript(index: Base.Index) -> Base.Iterator.Element {
return base[index]
}
// Note that Collection has default implementations for the rest of the
// requirements. You may want to explicitly implement them and forward them to
// the base collection though, as it's possible the base collection implements
// them in a more efficient manner (e.g being non random access and having
// a stored count).
}
// S adopts CollectionWrapper and satisfies the 'Base' associatedtype with [String].
struct S: CollectionWrapper {
var base: [String]
}
let s = S(base: ["foo", "bar", "baz"])
print(s[1]) // "bar"
Regarding your comment:
If I want to use this like that: let a: CollectionWrapper = S() [...] this leaves me with "Protocol can only be used as a generic constraint" again.
The problem is that you cannot currently talk in terms of a protocol with associated type requirements, as the types which are used to satisfy those requirements are unknown to the compiler (this isn't a technical limitation though). You can solve this by using Swift's AnyCollection type eraser to wrap an arbitrary Collection with a given element type.
For example:
let a = AnyCollection(S(base: ["foo", "bar", "baz"]))
If you need to work with additional protocol requirements from CollectionWrapper, you'll have to implement your own type eraser to do so. See Rob's answer here for an idea of how to go about this.

Related

Create a Swift extension with a where clause that filters on a struct that takes a generic

I'm trying to create an extension on Set that uses a where clause so that it only works on a struct I have that accepts a generic. But I keep running into errors about it the extension wanting the generic to be defined in the struct
In this example I'm getting the following error, with the compiler hint suggesting I use <Any>: Reference to generic type 'Test' requires arguments in <...>
struct Test<T> {
var value : T
func printIt() {
print("value")
}
}
extension Set where Element == Test {
}
However, when I use <Any> in the struct, I'm getting this error: Same-type constraint type 'Test' does not conform to required protocol 'Equatable'
extension Set where Element == Test<Any> {
}
Any suggestions on how to get the where clause to accept the Test struct for any type I'm using in the generic?
Thanks for your help
This is a limitation of Swift's type system. There's no way to talk about generic types without concrete type parameters, even when those type parameters are unrelated to the use of the type. For this particular situation (an extension for all possible type parameters), I don't believe there's any deep problem stopping this. It's a simpler version of parameterized extensions, which is a desired feature. It's just not supported (though there is an implementation in progress).
The standard way to address this today is with a protocol.
First, a little cleanup that's not related to your question (and you probably already know). Your example requires Test to be Hashable:
struct Test<T: Hashable>: Hashable {
var value : T
func printIt() {
print("value")
}
}
Make a protocol that requires whatever pieces you want for the extension, and make Test conform:
protocol PrintItable {
func printIt()
}
extension Test: PrintItable {}
And then use the protocol rather than the type:
extension Set where Element: PrintItable {
func printAll() {
for item in self { item.printIt() }
}
}
let s: Set<Test<Int>> = [Test(value: 1)]
s.printAll() // value
Just one more note on the error messages you're getting. The first error, asking you to add Any is really just complaining that Swift can't talk about unparameterized generics, and suggesting it's fallback type when it doesn't know what type to suggests: Any.
But Set<Any> isn't "any kind of Set." It's a Set where Element == Any. So Any has to be Hashable, which isn't possible. And a Set<Int> isn't a subtype of Set<Any>. There' completely different types. So the errors are a little confusing and take you down an unhelpful road.
This is not possible. The where clause requires a specific data type and simply passing a Test will not work unless I specify something more concrete like Test<String>.
Thank you to Joakim and flanker for answering the question in the comments
If you want to add extension for Set with where clause your Test must confirm to Hashable protocol.
Your Struct must look like this.
struct Test<T: Hashable> : Hashable {
var value : T
func printIt() {
print("value")
}
func hash(into hasher: inout Hasher) {
hasher.combine(value.hashValue)
}
}
So you can't use Any for your extension you must specify type that confirm to Hashable protocol.

Conform class extension to generic protocol function

* Short version *
How can I conform a class (extension) to a generic protocol function?
* Long version *
This is a small part of a data structure to support a paginated collection,
protocol Pageable {
//an object whose can be in a collection
}
protocol Page{ //a page of a collection that can be paginated
associatedtype PageItemType
func itemAt<PageItemType:Pageable>(index: Int) -> PageItemType
}
//Bonus question
//class PagedCollection<PageType:Page, ItemType:Pageable> {
//...
//}
Here is the implementation of the protocols with a "real" case:
class Person : Pageable{}
class People {
var people: [Person]?
}
//Mark: - Page
extension People: Page{ /*** error 1 ***/
typealias PageItemType = Person
func itemAt(index: Int) -> Person{
let person : Person = self.people![index]
return person
}
}
Obtaining the following error (1):
Type 'People' does not conform to protocol 'Page'
Protocol requires nested type 'PageItemType'
I also tried making it explicit but i just got a different error:
//Mark: - Page
extension People: Page{
typealias PageItemType = Person
func itemAt<PageItemType:Pageable>(index: Int) -> PageItemType{
let person : Person = self.people![index]
return person /*** error 2 ***/
}
}
Obtaining the following error (2):
Cannot convert return expression of type 'Person' to return type 'PageItemType'
So: *How can i let itemAt function return a valid type for the PageItemType typealias?
* Bonus *
Bonus question worth a 50 bounty (if answer is longer than a row i'll open a new question):
Referring to the first code snippet PagedCollection
given that each Page implementation has always a known implementation of Pageable protocol objet type
is there a way to avoid declaring ItemType:Pageable ? Or at least enforce it with a where clause?
It looks like you're conflating associated types with generic functions.
Generic functions allow you to provide a type to replace a given generic placeholder at the call site (i.e when you call the function).
Associated types allow types conforming to protocols to provide their own type to replace a given placeholder type in the protocol requirements. This is done per type, not at the call site of any function. If you wish to enforce a conformance requirement for an associatedtype, you should do so directly on its declaration (i.e associatedtype PageItemType : Pageable).
If I understand your requirements correctly, your itemAt(index:) function should be non-generic (otherwise the associatedtype in your protocol is completely redundant). The type that it returns is defined by the implementation of the type that conforms to Page, rather than the caller of the function. For example, your People class defines that the PageItemType associated type should be a Person – and that is what itemAt(index:) should return.
protocol Pageable {/* ... */}
protocol Page {
// any conforming type to Page will need to define a
// concrete type for PageItemType, that conforms to Pageable
associatedtype PageItemType : Pageable
// returns the type that the implementation of the protocol defines
// to be PageItemType (it is merely a placeholder in the protocol decleration)
func itemAt(index: Int) -> PageItemType
}
class Person : Pageable {/* ... */}
class People {
var people: [Person]?
}
extension People : Page {
// explicitly satisfies the Page associatedtype requirement.
// this can be done implicitly be the itemAt(index:) method,
// so could be deleted (and annotate the return type of itemAt(index:) as Person)
typealias PageItemType = Person
// the itemAt(index:) method on People now returns a Person
func itemAt(index: Int) -> PageItemType {
// I would advise against force unwrapping here.
// Your people array most likely should be non-optional,
// with an initial value of an empty array
// (do you really need to distinguish between an empty array and no array?)
let person = self.people![index]
return person
}
}
In regards to your implementation of a PagedCollection – as the PageItemType associated type in your Page protocol conforms to Pageable, I see no need for an ItemType generic parameter. This can simply be accessed through the associated type of the given PageType generic parameter.
As an example:
class PagedCollection<PageType:Page> {
// PageType's associatedtype, which will conform to Pageable.
// In the case of the PageType generic parameter being People,
// PageType.PageItemType would be of type Person
var foo : PageType.PageItemType
init(foo: PageType.PageItemType) {
self.foo = foo
}
}
// p.foo is of type Person, and is guarenteed to conform to Pageable
let p = PagedCollection<People>(foo: Person())
This is merely a formatted discussion from my chat with Hamish.
I'm writing it here, because chatrooms can get archived and sometimes a more direct QA can convey things differently
Me:
protocol Provider {
associatedtype Input
associatedtype Output
func value(forKey _key: Input) -> Output{}
}
vs.
protocol Provider{
func value<Input, Output>(forKey _key: Input) -> Output{}
}
Hamish: Essentially the difference is where the placeholders are satisfied – associated types are satisfied at type level, whereas generic placeholders on a function are satisfied at the call-site of said function.
Me: I understand that level of difference. But is there any byproduct due to that difference :D
Hamish: What do you mean? they express different things – the former is a protocol where the conforming type only deals with one specific type of input and output, the latter is a protocol where the conforming type can deal with any given input and output types.
Me: The former is a protocol where the conforming type only deals with one specific type of input and output. What do you mean by specific? Can't the typeAlias be anything I like?
Hamish: It can, but it can only be satisfied once per type – once I define
struct S : Provider {
func value(forKey: String) -> Int
}
S now can only deal with String inputs and Int outputs.
Whereas with the latter, I would define
struct S : Provider {
func value<Input, Value>(forKey _key: Input) -> Output{}
}
now S has to be able to deal with any input and output type.

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

Is it possible to add type constraints to a Swift protocol conformance extension?

I would like extend Array to add conformance to a new protocol — but only for arrays whose elements themselves conform to a specific protocol.
More generally, I’d like to have types (whether protocols or concrete types) with type parameters implement a protocol only when the type parameters match certain constraints.
As of Swift 2.0, this appears to be impossible. Is there a way I’m missing?
Example
Suppose we have the Friendly protocol:
protocol Friendly {
func sayHi()
}
We can extend existing types to implement it:
extension String: Friendly {
func sayHi() {
print("Greetings from \(self)!")
}
}
"Sally".sayHi()
We can also extend Array to implement sayHi() when its elements are all Friendly:
extension Array where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
["Sally", "Fred"].sayHi()
At this point, the type [Friendly] should itself implement Friendly, since it meets the protocol’s requirements. However, this code doesn’t compile:
extension Array: Friendly where Element: Friendly {
func sayHi() {
for elem in self {
elem.sayHi()
}
}
}
The error message is “extension of type 'Array' with constraints cannot have an inheritance clause,” which seems to shut the door definitively on that direct approach.
Is there an indirect workaround? Some clever trick I can use? Perhaps there’s a way that involves extending SequenceType instead of Array?
A working solution would make this code compile:
let friendly: Friendly = ["Foo", "Bar"]
Update: This has landed in Swift 4.1, and it is a thing of beauty!
The extension Array: Friendly where Element: Friendly example now compiles as given in the original question.
EDIT: As noted in the updated question, this is now possible since Swift 4.1
This is not currently possible in Swift (as of Xcode 7.1). As the error indicates, you can't restrict protocol conformance ("inheritance clause") to a type-constrained extension. Maybe someday. I don't believe there's any deep reason for this to be impossible, but it's currently not implemented.
The closest you can get is to create a wrapper type such as:
struct FriendlyArray<Element: Friendly>: Friendly {
let array: [Element]
init(_ array: [Element]) {
self.array = array
}
func sayHi() {
for elem in array {
elem.sayHi()
}
}
}
let friendly: Friendly = FriendlyArray(["Foo", "Bar"])
(You would likely want to extend FriendlyArray to be a CollectionType.)
For a tale of my own descent into the madness of trying to make this work, and my crawl back from the edge, see NSData, My Old Friend.
The good news is that what you are asking for Conditional Conformance is coming in Swift 4.1:
https://swift.org/blog/conditional-conformance/

Statically typed properties in Swift protocols

I'm trying to use Protocol-Oriented Pgrogramming for model layer in my application.
I've started with defining two protocols:
protocol ParseConvertible {
func toParseObject() -> PFObject?
}
protocol HealthKitInitializable {
init?(sample: HKSample)
}
And after implementing first model which conforms to both I've noticed that another model will be basically similar so I wanted to create protocol inheritance with new one:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
var value: AnyObject { get set }
}
A you can see this protocol has one additional thing which is value but I want this value to be type independent... Right now I have models which use Double but who knows what may show up in future. If I leave this with AnyObject I'm sentenced to casting everything I want to use it and if I declare it as Double there's no sense in calling this BasicModel but rather BasicDoubleModel or similar.
Do you have some hints how to achieve this? Or maybe I'm trying to solve this the wrong way?
You probably want to define a protocol with an "associated type",
this is roughly similar to generic types.
From "Associated Types" in the Swift book:
When defining a protocol, it is sometimes useful to declare one or
more associated types as part of the protocol’s definition. An
associated type gives a placeholder name (or alias) to a type that is
used as part of the protocol. The actual type to use for that
associated type is not specified until the protocol is adopted.
Associated types are specified with the typealias keyword.
In your case:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
typealias ValueType
var value: ValueType { get set }
}
Then classes with different types for the value property can
conform to the protocol:
class A : BasicModel {
var value : Int
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
class B : BasicModel {
var value : Double
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
For Swift 2.2/Xcode 7.3 and later, replace typealias in the
protocol definition by associatedtype.