Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Any Kind of sequence as a function parameter

I have created my custom sequence type and I want the function to accept any kind of sequence as a parameter. (I want to use both sets, and my sequence types on it)

Something like this:

private func _addToCurrentTileset(tilesToAdd tiles: SequenceType)

Is there any way how I can do it?

It seems relatively straightforward, but I can't figure it out somehow. Swift toolchain tells me: Protocol 'SequenceType' can only be used as a generic constraint because it has Self or associated type requirements, and I don't know how to create a protocol that will conform to SequenceType and the Self requirement from it.

I can eliminate the associatedType requirement with, but not Self:

protocol EnumerableTileSequence: SequenceType {
    associatedtype GeneratorType = geoBingAnCore.Generator
    associatedtype SubSequence: SequenceType = EnumerableTileSequence
}

Now if say I can eliminate self requirement, then already with such protocol definition other collectionType entities like arrays, sets won't conform to it.

Reference: my custom sequences are all subclasses of enumerator type defined as:

public class Enumerator<T> {

    public func nextObject() -> T? {
        RequiresConcreteImplementation()
    }
}

extension Enumerator {

    public var allObjects: [T] {
        return Array(self)
    }
}

extension Enumerator: SequenceType {

    public func generate() -> Generator<T> {
        return Generator(enumerator: self)
    }
}

public struct Generator<T>: GeneratorType {

    let enumerator: Enumerator<T>
    public mutating func next() -> T? {
        return enumerator.nextObject()
    }
} 
like image 569
ambientlight Avatar asked May 26 '16 10:05

ambientlight


People also ask

Can you pass a function as a parameter in Swift?

Every function in Swift has a type, consisting of the function's parameter types and return type. You can use this type like any other type in Swift, which makes it easy to pass functions as parameters to other functions, and to return functions from functions.

What is Inout parameter in Swift?

Swift inout parameter is a parameter that can be changed inside the function where it's passed into. To accept inout parameters, use the inout keyword in front of an argument. To pass a variable as an inout parameter, use the & operator in front of the parameter.

How do you declare a variable as a function in Swift?

Swift – Assign Function to a Variable To assign function to a variable in Swift, declare a variable that takes specific set/type of parameters and return a specific type of value. Then we can assign a function that has same type of parameters and return value to this variable.

What is the purpose of named function parameters in Swift?

The Swift Programming Language guide has the following suggestion: “The use of external parameter names can allow a function to be called in an expressive, sentence-like manner, while still providing a function body that is readable and clear in intent.”


2 Answers

The compiler is telling you the answer: "Protocol 'Sequence' can only be used as a generic constraint because it has Self or associated type requirements".

You can therefore do this with generics:

private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T) {
    ...
}

This will allow you to pass in any concrete type that conforms to Sequence into your function. Swift will infer the concrete type, allowing you to pass the sequence around without lose type information.

If you want to restrict the type of the element in the sequence to a given protocol, you can do:

private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T)  where T.Element: SomeProtocol {
    ...
}

Or to a concrete type:

private func _addToCurrentTileset<T: Sequence>(tilesToAdd tiles: T)  where T.Element == SomeConcreteType {
    ...
}

If you don't care about the concrete type of the sequence itself (useful for mixing them together and in most cases storing them), then Anton's answer has got you covered with the type-erased version of Sequence.

like image 167
Hamish Avatar answered Nov 11 '22 09:11

Hamish


You can use type-eraser AnySequence for that:

A type-erased sequence.

Forwards operations to an arbitrary underlying sequence having the same Element type, hiding the specifics of the underlying SequenceType.

E.g. if you will need to store tiles as an internal property or somehow use its concrete type in the structure of you object then that would be the way to go.

If you simply need to be able to use the sequence w/o having to store it (e.g. just map on it), then you can simply use generics (like @originaluser2 suggests). E.g. you might end up with something like:

private func _addToCurrentTileset<S: SequenceType where S.Generator.Element == Tile>(tilesToAdd tiles: S) {
    let typeErasedSequence = AnySequence(tiles) // Type == AnySequence<Tile>
    let originalSequence = tiles // Type == whatever type that conforms to SequenceType and has Tile as its Generator.Element
}
like image 37
0x416e746f6e Avatar answered Nov 11 '22 10:11

0x416e746f6e