Recursively parse a CollectionType - swift

I'm writing a recursive descent parser. I'd like my parser to work on any (or at least "many") Collections of UInt8 (e.g., not only Swift.Array)
func unpack<T: CollectionType where T.Generator.Element == UInt8>(t: T) {
let m = t.dropFirst()
//[do actual parsing here]
unpack(m)
}
However:
error: cannot invoke 'unpack' with an argument list of type '(T.SubSequence)'
note: expected an argument list of type '(T)'
This is puzzling because:
dropFirst returns Self.SubSequence
CollectionType.SubSequence is SubSequence : Indexable, SequenceType = Slice<Self>
Slice is CollectionType.
Therefore, m should be CollectionType.
However for some reason, this doesn't work. How do I define unpack so it can be recursively passed subsequences?

There is no CollectionType in Swift anymore. Both Array and ArraySlice adopt Sequence. And the dropFirst() method which you use is declared in Sequence. So you can make recursive generic function like this:
func unpack<T: Sequence>(t: T) where T.Element == UInt8 {
let m = t.dropFirst()
//[do actual parsing here]
unpack(m)
}

Related

What is "Element" type?

Reading Swift Programming Language book I've seen number of references to type Element, which is used to define type of collection items. However, I can't find any documentation on it, is it class, protocol? What kind of functionality/methods/properties it has?
struct Stack<Element>: Container {
// original Stack<Element> implementation
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
...
If we tried trace the idea of how we do even get Element when working with collections, we would notice that it is related to the Iterator protocol. Let's make it more clear:
Swift Collection types (Array, Dictionary and Set) are all conforms to Collection protocol. Therefore, when it comes to the Collection protocol, we can see that the root of it is the Sequence protocol:
A type that provides sequential, iterated access to its elements.
Sequence has an Element and Iterator associated types, declared as:
associatedtype Element
associatedtype Iterator : IteratorProtocol where Iterator.Element == Element
You could review it on the Sequence source code.
As shown, Iterator also has an Element associated type, which is compared with the sequence Element, well what does that means?
IteratorProtocol is the one which does the actual work:
The IteratorProtocol protocol is tightly linked with the Sequence
protocol. Sequences provide access to their elements by creating an
iterator, which keeps track of its iteration process and returns one
element at a time as it advances through the sequence.
So, Element would be the type of returned element to the sequence.
Coding:
To make it simple to be understandable, you could implement such a code for simulating the case:
protocol MyProtocol {
associatedtype MyElement
}
extension MyProtocol where MyElement == String {
func sayHello() {
print("Hello")
}
}
struct MyStruct: MyProtocol {
typealias MyElement = String
}
MyStruct().sayHello()
Note that -as shown above- implementing an extension to MyProtocol makes MyElement associated type to be sensible for the where-clause.
Therefore sayHello() method would be only available for MyProtocol types (MyStruct in our case) that assign String to MyElement, means that if MyStruct has been implemented as:
struct MyStruct: MyProtocol {
typealias MyElement = Int
}
you would be not able to:
MyStruct().sayHello()
You should see a compile-time error:
'MyStruct.MyElement' (aka 'Int') is not convertible to 'String'
The same logic when it comes to Swift collection types:
extension Array where Element == String {
func sayHello() {
print("Hello")
}
}
Here is the definition in the Apple documentation
Element defines a placeholder name for a type to be provided later.
This future type can be referred to as Element anywhere within the
structure’s definition.
Element is usually used as the generic type name for collections, as in
public struct Array<Element> { ... }
so it is what you construct your array from, and not something predefined by the language.
Element is a purpose-built (and defined) placeholder for structures. Unlike some of the answers/comments have suggested, Element cannot always be substituted for T because T without the proper context is undefined. For example, the following would not compile:
infix operator ++
extension Array {
static func ++ (left: Array<T>, right: T) -> Array {
...
}
}
The compiler doesn't know what T is, it's just an arbitrary letter—it could be any letter, or even symbol (T has just become Swift convention). However, this will compile:
infix operator ++
extension Array {
static func ++ (left: Array<Element>, right: Element) -> Array {
...
}
}
And it compiles because the compiler knows what Element is, a defined placeholder, not a type that was arbitrarily made up.

Swift not finding the correct type

I am trying to use SwiftHamcrest
I have a function
func equalToArray<T, S>(_ vector:Array<S>) -> Matcher<T> {
let v: Matcher<T> = Hamcrest.hasCount(16)
return v
}
This gives an error
Error:(16, 31) 'hasCount' produces 'Matcher<T>', not the expected contextual result type 'Matcher<T>'
SwiftHamcrest has two hasCount functions
public func hasCount<T: Collection>(_ matcher: Matcher<T.IndexDistance>) -> Matcher<T>
public func hasCount<T: Collection>(_ expectedCount: T.IndexDistance) -> Matcher<T>
Why is my code complaining isn't it returning the same type that is needed.
As a note and possibly a different question I had to add the Hamcrest. before the hasCount method call as otherwise it tried to match to the first function
What am I missing with types?
Your method equalToArray<T, S> does not know that T is a collection, so the result from the generic hasCount(...) methods above will not be assignable to v in your method (since these results returns Matcher<T> instances constrained to T:s that are Collection:s). I.e., v is of a type Matcher<T> for a non-constrained T, meaning, in the eyes of the compiler, there is e.g. no T.IndexDistance for the T of v:s type.
If you add a Collection type constraint to the T of your method, the assignment from hasCount(...) result to v should compile:
func equalToArray<T: Collection, S>(_ vector: Array<S>) -> Matcher<T> {
let v: Matcher<T> = Hamcrest.hasCount(16)
return v
}
In a perfect world, the compiler could've given us a more telling error message, say along the lines of
Error:(16, 31) 'hasCount' produces 'Matcher<T>' where 'T: Collection',
not the expected contextual result type 'Matcher<T>'
Now, I don't know what you're intending to test here, but as #Hamish points out, you might actually want to return a Matcher<[S]> and drop the T placeholder. E.g. using the count property of the supplied vector parameter as argument to hasCount(...)?
func equalToArray<S>(_ vector: Array<S>) -> Matcher<[S]> {
return hasCount(vector.count)
}
Not having used Hamcrest myself, I might be mistaken, but based on a quick skim over the SwiftHamcrest docs, I believe equalToArray(_:) defined as above would construct a matcher for "vector equality" (w.r.t. semantics of the function name) based only on the count of two vectors, in which case the following assert would be a success
let arr1 = ["foo", "bar"]
let arr2 = ["bar", "baz"]
assertThat(arr1, equalToArray(arr2)) // success! ...
But this is just a byline, as you haven't shown us the context where you intend to apply your equalToArray(_:) method/matcher; maybe you're only showing us a minimal example, whereas the actual body of you custom matcher is more true to the method's name.

Functions on generic arrays

I would like to use more functional programming in Swift. Some of the functions I write could work well on Arrays of various types. I don't want to rewrite the same function with different types (or typealiases.) The pattern how the function would work is often the same, just with different types. So, I tried something like this:
// Add indeces to an array of any type. I.e., returns an array of tuples of the array index and the original element.
func addIndeces<T: AnyObject>(toArray: Array<T>) -> Array<(index: Int, value: T)> {
var arrIndex: [Int] = []
for index in 0...toArray.count {
arrIndex.append(index)
}
return Array(Zip2(arrIndex, toArray))
}
When I call this function
// Note: reminderList is of type [Reminder]
let indexedReminderList = addIndeces(reminderList) as! [(index: Int, reminder: Reminder)]
I get a runtime error: "fatal error: can't unsafeBitCast between types of different sizes"
What am I doing wrong?
The function you are writing already exists – kind of. enumerate "return a lazy SequenceType containing pairs (n, x), where n\ s are consecutive Int\ s starting at zero, and x\ s are the elements of base"
This means you can write your function as:
func addIndices<T>(toArray: [T]) -> [(index: Int, reminder: T)] {
// if you want to label the tuple elements index: and reminder:,
// you still have to use map:
return map(enumerate(toArray)) {
(index: $0, reminder: $1)
}
}
Note, you don’t need to write T: AnyObject unless you specifically want to prevent this function from accepting anything other than arrays of classes (as that's what AnyObject is - the protocol that only classes, not structs or enums, conform to).
Note, enumerate only works for integer-indexed collections. To make it more general, you could write zip(indices(someCollection),someCollection)). indices returns a range of all the indices of any collection, so is equivalent to someCollection.startIndex..<someCollection.endIndex.
You want to cast the tuple Array of type [(index: Int, value: T)] where T is of type Reminder to a tuple Array of type [(index: Int, reminder: Reminder)]. So you can see that the tuples have different element names (value and reminder) where both have different byte sizes - therefore the error.
So you should take the same element names for both tuples.
If you want to change the element names:
let newTupleArray = oldTupleArray.map{ (newElementName0: $0.0, newElementName1: $0.1) }
The range 0...toArray.count consist all elements form 0 to the count of toArray, including the count value, and in your case arrIndex will always have one more element then toArray. Thats what is causing different size error. To fix it replace your range with this one 0..<toArray.count

Check array of optionals for nil

I have a number of optionals and would like to efficiently check to see if any of them is nil.
Ideally...
if contains([optional1, optional2, optional3], nil) { /* foo */ }
But swift won't let me do this. (Type 'S.Generator.Element -> L' does not conform to protocol 'NilLiteralConvertible')
And optionals can't be AnyObjects so I can't cast it to an NSArray either (to use .containsObject).
I could just write a for loop but that seems like bad style. Any suggestions?
There are two contains() functions:
func contains<S : SequenceType where S.Generator.Element : Equatable>(seq: S, x: S.Generator.Element) -> Bool
func contains<S : SequenceType, L : BooleanType>(seq: S, predicate: #noescape (S.Generator.Element) -> L) -> Bool
The first one takes an element as the second argument, but
requires that the elements conform to the Equatable
protocol, which is not the case for optionals.
The second one takes a predicate as second argument, and that
can be used here:
let optArray : [Int?] = [1, nil, 2]
if contains(optArray, { $0 == nil } ) {
}
But any solution must traverse the array until a nil element is
found, so the above solution may be better readable but is not
necessarily more efficient than a for-loop.

Compare two objects in Swift

How can I compare two generic objects. Below is sample code of doing a comparison and this line elem > value throws an error saying Could not find overload for '>' that accepts the supplied arguments
func index<T : Equatable>(array: T[], value: T) -> Int {
for (index, elem) in enumerate(array) {
if elem > value {
return index
}
}
return array.count
}
From the Swift Reference:
The Equatable protocol makes it possible to determine whether two values of the same type are considered to be equal.
There is one required operator overload defined in the protocol: ==.
There is no guarantee that Equatable objects have to implement the > operator, which explains your error.
Take a look at Comparable however. Notice that comparable only needs to overload the < and == operators.
However, if not a < b nor is a == b, you can assume that a > b.
You want Comparable, not Equatable. Equatable only has ==.