Swift "where" Array Extensions - swift

As of Swift 2.0 it seems we can get closer to extensions of generic types applicable to predicated situations.
Although we still can't do this:
protocol Idable {
var id : String { get }
}
extension Array where T : Idable {
...
}
...we can now do this:
extension Array {
func filterWithId<T where T : Idable>(id : String) -> [T] {
...
}
}
...and Swift grammatically accepts it. However, for the life of me I cannot figure out how to make the compiler happy when I fill in the contents of the example function. Suppose I were to be as explicit as possible:
extension Array {
func filterWithId<T where T : Idable>(id : String) -> [T] {
return self.filter { (item : T) -> Bool in
return item.id == id
}
}
}
...the compiler will not accept the closure provided to filter, complaining
Cannot invoke 'filter' with an argument list of type '((T) -> Bool)'
Similar if item is specified as Idable. Anyone had any luck here?

extension Array {
func filterWithId<T where T : Idable>(id : String) -> [T] {
...
}
}
defines a generic method filterWithId() where the generic
placeholder T is restricted to be Idable. But that definition introduces a local placeholder T
which is completely unrelated to the array element type T
(and hides that in the scope of the method).
So you have not specified that the array elements must conform
to Idable, and that is the reason why you cannot call
self.filter() { ... } with a closure which expects the elements
to be Idable.
As of Swift 2 / Xcode 7 beta 2, you can define extension methods on a generic type which are more restrictive on the template
(compare Array extension to remove object by value for a very similar issue):
extension Array where Element : Idable {
func filterWithId(id : String) -> [Element] {
return self.filter { (item) -> Bool in
return item.id == id
}
}
}
Alternatively, you can define a protocol extension method:
extension SequenceType where Generator.Element : Idable {
func filterWithId(id : String) -> [Generator.Element] {
return self.filter { (item) -> Bool in
return item.id == id
}
}
}
Then filterWithId() is available to all types conforming
to SequenceType (in particular to Array) if the sequence element
type conforms to Idable.
In Swift 3 this would be
extension Sequence where Iterator.Element : Idable {
func filterWithId(id : String) -> [Iterator.Element] {
return self.filter { (item) -> Bool in
return item.id == id
}
}
}

Related

how to store away sequence variables with constraints in swift

I wanted to create a "where_non_null" operation that works on any swift sequence - which is easy if you return an array, but obviously that is potentially bad performance wise - because you are forcing the entire sequence to resolve in memory - so I created the following that just goes line by line:
//
// this iterates through the underlying sequence, and returns only the values that are not null
//
public class Not_null_iterator<T> : IteratorProtocol
{
public typealias Element = T
private let next_function : () -> T?
init<T_iterator: IteratorProtocol>( _ source: T_iterator ) where T_iterator.Element == Optional<T>
{
var iterator = source
next_function =
{
while (true)
{
if let next_value = iterator.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
else
{
return nil
}
}
}
}
public func next() -> T? {
next_function()
}
}
//
// a sequence wrapping an underlying sequence, that removes any nulls as we go through
//
public class Not_null_sequence<T > : Sequence
{
private var iterator_creator : () -> Not_null_iterator<T>
init<T_source_sequence : Sequence >( _ source : T_source_sequence ) where T_source_sequence.Element == Optional<T>
{
iterator_creator =
{
Not_null_iterator(source.makeIterator())
}
}
public func makeIterator() -> Not_null_iterator<T>
{
iterator_creator()
}
}
extension Sequence
{
//
// return only the not null values in the sequence without ever resolving more than one item in memory at one time and remove the optionality on the type
//
func where_not_null<T>() -> Not_null_sequence<T> where Element == Optional<T>
{
return Not_null_sequence( self)
}
}
class Where_not_null_tests : XCTestCase
{
public func test_where_not_null()
{
let source = [1, 2, 3, nil, 4]
let checked : [Int] = Array(source.where_not_null())
XCTAssertEqual([1,2,3,4],checked)
}
}
which works great - however I had to define the next() and make_iterator() functions in the constructor, because I couldn't find any type safe way of putting the source into a class level variable.
Is there a way of doing that?
[and yes, I'm aware swift people prefer camel case]
Rather than just using one generic parameter, you'd need two generic parameters. You can't just constrain one generic parameter to say that it has to be some sequence with an element of some Optional. You need another generic parameter to say what the optional's type is:
class NotNilIterator<T: IteratorProtocol, U>: IteratorProtocol where T.Element == U? {
typealias Element = U
var iterator: T
init(_ source: T) {
iterator = source
}
func next() -> Element? {
// I feel this is clearer what is going on
while true {
switch iterator.next() {
case .some(.none):
continue
case .none:
return nil
case .some(.some(let element)):
return element
}
}
}
}
class NotNilSequence<T: Sequence, U> : Sequence where T.Element == U?
{
let sequence: T
init(_ source : T)
{
sequence = source
}
public func makeIterator() -> NotNilIterator<T.Iterator, U>
{
.init(sequence.makeIterator())
}
}
whereNotNil would then be declared like this:
func whereNotNil<T>() -> NotNilSequence<Self, T> where Self.Element == T?
{
return .init(self)
}
Note the use of self types. The first parameter is the type of the underlying sequence, the second is the non-optional type.
Note that this sort of "lazily computed sequence" is already built into Swift. To lazily filter out the nils, do:
let array = [1, 2, 3, nil, 4]
let arrayWithoutNil = array.lazy.compactMap { $0 }
The downside is that the type names are quite long. arrayWithoutNil is of type
LazyMapSequence<LazyFilterSequence<LazyMapSequence<LazySequence<[Int?]>.Elements, Int?>>, Int>
But you can indeed get non-optional Ints out of it, so it does work.
The way swift generics work can sometimes be very confusing (but has it's advantages). Instead of declaring that a variable is of a generic protocol (resp. a protocol with associated types), you instead declare another generic type which itself conforms to your protocol. Here's your iterator as an example (I have taken the liberty to clean up the code a bit):
public class Not_null_iterator<T, T_iterator> : IteratorProtocol where
T_iterator: IteratorProtocol,
T_iterator.Element == Optional<T>
{
private var source: T_iterator
init(_ source: T_iterator) {
self.source = source
}
public func next() -> T? {
while let next_value = source.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
return nil
}
}
The non-null sequence works analogous:
public class Not_null_sequence<T, Source>: Sequence where
Source: Sequence,
Source.Element == Optional<T>
{
private var source: Source
init(_ source: Source) {
self.source = source
}
public func makeIterator() -> Not_null_iterator<T, Source.Iterator> {
Not_null_iterator(self.source.makeIterator())
}
}
Using this some IteratorProtocol is just a nice way to let the compiler figure out the type. It is equivalent to saying Not_null_iterator<T, Source.Iterator>
As a (potentially) interesting side-note, to clean up the generic mess even more, you can nest the iterator class inside the Not_null_sequence:
public class Not_null_sequence<T, Source>: Sequence where
Source: Sequence,
Source.Element == Optional<T>
{
private var source: Source
init(_ source: Source) {
self.source = source
}
public func makeIterator() -> Iterator{
Iterator(self.source.makeIterator())
}
public class Iterator: IteratorProtocol {
private var source: Source.Iterator
init(_ source: Source.Iterator) {
self.source = source
}
public func next() -> T? {
while let next_value = source.next()
{
if let not_null_value = next_value
{
return not_null_value
}
}
return nil
}
}
}

Referencing instance method 'stringify()' on 'Collection' requires the types 'Int' and 'Stringify' be equivalent

I made an protocol Stringify to convert Types that implement the protocol to a String.
protocol Stringify {
func stringify() -> String
}
extension Collection where Iterator.Element == Stringify {
/// changes all the elements in the collection to a String
func stringify() -> [String] {
var strings = [String]()
if let elements = self as? [Stringify] {
for element in elements {
strings.append(element.stringify())
}
}
return strings
}
}
extension Int: Stringify {
func stringify() -> String {
return String(self)
}
}
extension Double: Stringify {
func stringify() -> String {
return String(self)
}
}
let test = [5,6,7]
/// does work
[6,5,34].stringify()
/// does not work -> Error alert
test.stringify()
But when I set a collection of Ints to a property and using then stringify() on it, it does not work.
Error:
Referencing instance method 'stringify()' on 'Collection' requires the
types 'Int' and 'Stringify' be equivalent
If I use it directly everything is going fine.
What is the problem here?
extension Collection where Iterator.Element == Stringify
has a “same type requirement” and defines an extension for collections whose elements are of the type Stringify. But test is an array of Int, i.e. the elements conform to the Stringify protocol. So what you want is
extension Collection where Iterator.Element : Stringify
or, equivalently,
extension Collection where Element : Stringify
The reason that
/// does work
[6,5,34].stringify()
compiles with your original definition is that the compiler infers the type of the array as [Stringify] from the context.
let test: [Stringify] = [5,6,7]
test.stringify()
would compile as well.
Note that there is no need to cast self in the extension method. You can simplify the implementation to
func stringify() -> [String] {
var strings = [String]()
for element in self {
strings.append(element.stringify())
}
return strings
}
or just
func stringify() -> [String] {
return self.map { $0.stringify() }
}

Swift Array extension with generic function parameter

I am using Swift 4 and looking for a way to create extension function for array collection with arguments of type
typealias Listener<T> = (T) -> Void
however extension below cannot be created (Use of undeclared type 'T')
extension Sequence where Element == Listener<T>{
func callAll(t: T){
self.forEach { $0(t) }
}
}
Is there a way to make it work?
You cannot introduce new generic parameters at the header of an extension like T in your code, but each method can have generic parameters.
typealias Listener<T> = (T) -> Void
extension Sequence {
func callAll<T>(t: T)
where Element == Listener<T>
{
self.forEach { $0(t) }
}
}
let listeners: [Listener<Int>] = [
{ print($0) },
{ print($0 * 2) },
]
listeners.callAll(t: 2)

conforming to Sequence and IteratorProtocol in Swift

I am trying to write my own version of IndexingIterator to increase my understanding of Sequence. I haven't assign any type to associatetype Iterator in my struct. However, the complier doesn't complain about that and I get a default implementation of makeIterator.
Following are my codes:
struct __IndexingIterator<Elements: IndexableBase>: Sequence, IteratorProtocol {
mutating func next() -> Elements._Element? {
return nil
}
}
let iterator = __IndexingIterator<[String]>()
// this works and returns an instance of __IndexingIterator<Array<String>>. why?
iterator.makeIterator()
I think there must be some extensions on Sequence which add the default implementation. Thus, I searched it in Sequence.swift and only found this.
extension Sequence where Self.Iterator == Self, Self : IteratorProtocol {
/// Returns an iterator over the elements of this sequence.
public func makeIterator() -> Self {
return self
}
}
I thought it would be like this:
extension Sequence where Self: IteratorProtocol {
typealias Iterator = Self
...
}
Did I miss something or I misunderstood the extension?
It looks like Alexander's answer is correct. Here's a boiled down version, without using Sequence:
protocol MySequence {
associatedtype Iterator: IteratorProtocol
func maakeIterator() -> Iterator
}
extension MySequence where Self.Iterator == Self, Self : IteratorProtocol {
/// Returns an iterator over the elements of this sequence.
func maakeIterator() -> Self {
return self
}
}
struct __IndexingIterator<Element>: MySequence, IteratorProtocol {
mutating func next() -> Element? {
return nil
}
}
let iterator = __IndexingIterator<[String]>()
iterator.maakeIterator()
You can write your own Iterator which confrom to IteratorProtocol first, then write what you need confrom to Sequence.
Make sure that you have to implement requried func.
struct IteratorTest : IteratorProtocol {
typealias Element = Int
var count : Int
init(count :Int) {
self.count = count
}
mutating func next() -> Int? {
if count == 0 {
return nil
}else {
defer {
count -= 1
}
return count;
}
}
}
struct CountDown : Sequence {
typealias Iterator = IteratorTest
func makeIterator() -> IteratorTest {
return IteratorTest.init(count: 10)
}
}
The type alias isn't necessary, because the Element associated type is inferred from your implementation of next().
Here's a simple example:
protocol ResourceProvider {
associatedtype Resoruce
func provide() -> Resoruce;
}
struct StringProvider {
func provide() -> String { // "Resource" inferred to be "String"
return "A string"
}
}

What is the type of an array of SequenceTypes generating the same type of class

I have a bunch of classes that are very different, except they are all capable of returning objects of type MyProtocol.
So for each class I have
extension MyClass : SequenceType {
func generate() -> GeneratorOf<MyProtocol> {
var index = -1;
return GeneratorOf< MyProtocol > {
index += 1
return index < self.values.count
? self.values[index]
: nil
}
}
}
I want to pass a collection of such classes to a function that can do
func printAll (containers : ????) {
for container : containers {
for myProtocolValue : container {
print (myProtocolValue.myProtocolFunc())
}
}
}
What type does containers have?
The best solution would be to make a new protocol. All of your classes conform to it and it has no self requirements in order to use heterogeneous Arrays:
protocol MyProtocolContainer {
func generate() -> GeneratorOf<MyProtocol>
}
// the implementation of the generate function stays the same but you conform to MyProtocolContainer
extension MyClass : SequenceType, MyProtocolContainer { ... }
// the printAll function has the following implementation
func printAll (containers : [MyProtocolContainer]) {
for container in containers {
// you have to call generate manually since MyProtocolContainer is no SequenceType
// in order to be used in a heterogenous collection
for myProtocolValue in container.generate() {
print (myProtocolValue.myProtocolFunc())
}
}
}
Side note:
in Swift there are no for ... : ... loops the : should be an in.
To make the implementation of the generate function easier I would suggest to use the Generator of the Array itself:
class MyClass: MyProtocolReturnable, SequenceType {
func generate() -> GeneratorOf<MyProtocol> {
return GeneratorOf(self.values.map{ $0 as MyProtocol }.generate())
// if the array is of type [MyProtocol] you can also use
return GeneratorOf(self.values.generate())
}
}