Swift - create a fixed length array enforced at compile time

空扰寡人 提交于 2019-12-04 19:47:28

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.

Kametrixom

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!