2

I want to enforce (compile time) array with 5 elements of a particular type

I couldn't find a solution so resorted to a workaround by creating a tuple

(This is abusive I know)

typealias FiveElementArray = (MyType,MyType,MyType,MyType,MyType) // mock array by using typed tuple

It works for my needs - until I need to access an element by index at runtime.

For instance:

var DB = FiveElementArray // the tuple of 5 elements

tableView(tableView : UITableView,cellForRowAtIndexPath:indexPath) -> UITableViewCell {
// would like to populate with the value at index
DB[indexpath.row] // no such syntax for tuples

}

So how can I use a proper swift Array with statically typed length

5
  • Have you tried assigning the array via let instead? Commented May 4, 2015 at 16:39
  • If you create an array, a set, or a dictionary and assign it to a variable, the collection that is created will be mutable. This means that you can change (or mutate) the collection after it is created by adding, removing, or changing items in the collection. Conversely, if you assign an array, a set, or a dictionary to a constant, that collection is immutable, and its size and contents cannot be changed. From developer.apple.com/library/ios/documentation/Swift/Conceptual/… Commented May 4, 2015 at 16:41
  • @luk2302 It doesn't answer the question (it's not related). Commented May 4, 2015 at 16:48
  • The collection will be immutable ... I would guess that means that you can not change it anymore => fixed length?! Commented May 4, 2015 at 16:49
  • Im not interested in the immutability, rather the promise of the array length (used as a parameter and return value ) Commented May 4, 2015 at 16:53

2 Answers 2

8

The way to prevent unwanted changes on a value type (such as an array) is to put a didSet observer on it to act as a guard:

var arr = [1, 2, 3, 4, 5] {
    didSet {
        if arr.count > 5 {arr = oldValue}
    }
}
arr.append(6)
println(arr) // [1, 2, 3, 4, 5], that change was illegal
arr[2] = 100
println(arr) // [1, 2, 100, 4, 5], that change was legal

But if that isn't good enough, you'll need to use a wrapper, i.e. has-an array rather than is-an array:

struct FiveElementArray<T> {
    private var arr = Array<T>()
    // methods for access go here
}

The "methods for access" can include implementations of all the ways of modifying that array that you do permit (e.g. subscripting) and can simply not implement all the ways that you don't permit (e.g. append and extend).

If you insist on a compile-time check, then just stick with your tuple, or even write your own collection. But at that point I think you are just being silly with your requirements. The struct wrapper prevents unwanted changes; thus it is a guarantee, so there is no need to make the guarantee at compile time.

Sign up to request clarification or add additional context in comments.

6 Comments

Thought about that but i want to be able to guarantee that a method returning an array will always have 5 elements. For instance func doSomething() -> FiveElementArray
Well, then you'll have to define your own collection type (which is almost what you are doing now), or wrap your collection in a struct that acts as a guard.
But i would loose my compile time static check
You have no compile time static check. Okay, you do with the tuple. And that's as good as it gets.
I'm sorry but I think your requirements here are silly, as I've said. I've given several perfectly good alternatives. Wrapping the array in a struct would solve the problem, allowing subscripting and preventing unwanted types of modification, so why not just do it? Your requirement that this be protected at compile time is just unnecessary, when the guard struct can perform the protection perfectly well at runtime in an inviolable fashion; no compile-time protection is needed because no runtime violation is going to happen. So just solve the darned problem the easy way and move on.
|
1

You're probably looking for Dependent Types: It enables you to encode some value (the array length) into the type itself. Luckily it's possible to use dependent types in Swift to some extent.

@oisdk also wrote a blog post where he creates an efficient dependently typed collection structure, you can also read the article while interacting with the code in the Playground version.

Example usage:

func onlySameLength<A, B, L : Nat>(lhs: ConstArray<A, L>, rhs: ConstArray<B, L>) {}

let twoLong = emptyArray() +| 1 +| 2         // [1, 2]

let twoChar = emptyArray() +| "a" +| "b"     // ["a", "b"]

onlySameLength(twoLong, rhs: twoChar)

let threeInts = emptyArray() +| 1 +| 2 +| 3  // [1, 2, 3]

//Uncomment for an error
//onlySameLength(twoLong, rhs: threeInts)

I strongly suggest to use Swift's type inference for this one as the type of a ConstArray with elements String and count 3 would be ConstArray<String, Succ<SuccSucc<<Zero>>>>

Okay, something like [1, 2].lazy.filter {$0 == 1 }.flatMap{ [$0] } has type LazyCollection<FlattenCollection<LazyMapCollection<LazyFilterCollection<Array<Int>>, [Int]>>>, so I guess ConstArray isn't too bad :P

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.