Define a Swift Protocol Which Requires a Specific Type of Sequence

Define a Swift protocol which requires a specific type of sequence

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

Requiring, for a protocol, that an instance variable conform to a protocol ; rather than have a specific type

What you're looking for is an associated type. This means exactly what you've described (the required type conforms to a protocol rather than being the existential of that protocol):

protocol CompatibleFloatingPoint {
associatedtype BitPattern: FixedWidthInteger
var bitPattern : BitPattern { get }
}

extension Float: CompatibleFloatingPoint {}
extension Double: CompatibleFloatingPoint {}

For more details, see Associated Types in the Swift Programming Language.

Function that accepts sequence of type

Your question is to do with the difference between generic types and associated types. See here (sections titled "Generic Types", "Associated Types") for a basic explanation of their purposes in Swift. Protocols, like Sequence, use associated types, rather than generic types. Your first code sample would make sense if Sequence was a concrete class with a generic type - this difference should explain the error message.

As for why protocols use associated types, rather than generic types, see the top answer to this question. Essentially, though they seem to serve the same purpose, associated types are meant to be more flexible and descriptive, where generic types are about implementation. This makes your code sample more verbose, but overall, makes many code samples simpler.

In fact, from the Sequence source code, Sequence has an associated type Iterator, which conforms to the IteratorProtocol protocol, which in turn has its own associated type Element (which can be any type).

How does Swift infer Sequence requirements when Self implements IteratorProtocol?

Let's start with the implementation of IteratorProtocol.next. The compiler sees this implementation:

mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}

And notices that it returns an Int?. Well, IteratorProtocol.next is supposed to return a Self.Element?, so it infers that IteratorProtocol.Element == Int. Now Coundown satisfies IteratorProtocol.

Note that Sequence and IteratorProtocol share the associated type Element. Once Swift figures out the witness for IteratorProtcol.Element, it's as if you declared a new type alias Element in Countdown, and it just so happens that Sequence requires that Countdown.Element to exist.

After that, the compiler infers Iterator == Self. This is so that the default implementation of makeIterator is available. However, it is quite a mystery how the compiler can infer this, because with only these information, the type can't normally be inferred, as can be shown by creating your own sequence and iterator protocols.

protocol MyIterator {
associatedtype Element

mutating func next() -> Element?
}

protocol MySequence {
associatedtype Element where Element == Iterator.Element
associatedtype Iterator : MyIterator

func makeIterator() -> Iterator
}

extension MySequence where Self == Self.Iterator {
func makeIterator() -> Iterator {
return self
}
}

struct Countdown: MySequence, MyIterator { // doesn't compile
var count: Int

mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}

After looking into the source code, I suspect there might be some compiler magic going on, especially here:

// Provides a default associated type witness for Iterator when the
// Self type is both a Sequence and an Iterator.
extension Sequence where Self: IteratorProtocol {
// @_implements(Sequence, Iterator)
public typealias _Default_Iterator = Self
}

This seems to set a "preferred" type for Iterator to be inferred as. It seems to be saying "When Iterator can't be inferred to be anything, try Self". I can't find _Default_Iterator anywhere else, which is why I concluded it's compiler magic. The whole purpose of this is to allow you to conform to Sequence by only conforming to IteratorProtocol and implementing next, as the documentation said you can do.

Now that Iterator == Self, we have also satisfied the constraint on Element:

associatedtype Element where Self.Element == Self.Iterator.Element

Thus we have shown that Countdown conforms to Sequence.

Swift: Type does not conform to protocol 'ArrayLiteralConvertible'

ArrayLiteralConvertible requires you to implement an initializer of type init(arrayLiteral: Element...). Something like this would re-use your initializer that takes a sequence:

init(arrayLiteral: Element...) {
self = Set(arrayLiteral)
}

If you’re doing this in a playground, hit opt-cmd-enter to see more details in the assistant editor than you get just from the error message in the source. It shows the details of all the protocol requirements you aren’t meeting.

By the way, if you declared contents like this:

private var contents: [Element: Bool] = [:]

you wouldn’t need to initialize it in each of your initializers.

Swift, use generic property with different specific types - Reference to generic type requires arguments in

You can specify a protocol for providing Stage types like so:

protocol StageProvider {

associatedtype T: Stage

func getType() -> T.Type

}

Then make your SomethingThatKnowsAboutTheStages or any other one conform this protocol:

class SomethingThatKnowsAboutTheStages: StageProvider {

typealias T = SecondStage

func getType() -> T.Type {
SecondStage.self
}

}

Add an initializer for your FooBarController:

class FooBarController<StageType: Stage>: UIViewController {

convenience init(stage: StageType.Type) {
self.init()
}

}

And finally use all these:

func fooBarScreen<T: StageProvider>(boop: T) {
let controller = FooBarController(stage: boop.getType())
}

Swift Protocol inheriting Protocol method with same name

This is because a non-throwing function is by definition a sub-type of a throwing function

From the Swift Programming Language book

The throws keyword is part of a function’s type, and nonthrowing functions are subtypes of throwing functions. As a result, you can use a nonthrowing function in the same places as a throwing one.

But you can't do it the other way around so the below code will generate an error

protocol Base {
func foo() -> Int
}

protocol Refined: Base {
func foo() throws -> Int //error: Cannot override non-throwing method with throwing method
}

Also note that this is not only for protocols, if you remove the func declaration from Refined you can still implement the function in Test as non throwing.



Related Topics



Leave a reply



Submit