jeudi 19 novembre 2015

Complex application of a Swift typealias'd protocol in Swift

In Swift, I am trying to define a transpose operation for a Matrix class which has a row view and a column view. The approach I am trying to take returns a new instances of Matrix with the opposite typed view. A much simplified (though, still long) version of the code:

public protocol Storage {
    typealias ElementType

    init(size: Int)
}

public class NativeStorage<E>: Storage {
    public typealias ElementType = E

    public required init(size:Int) {}
}


public protocol StorageView {
    typealias StorageType:Storage
    typealias TransposeType

    var storage:StorageType { get }
    var window:[Range<Int>] { get }

    init(storage:StorageType, window:[Range<Int>])

    func transpose() -> TransposeType
}

A quick note here, ideally I would like to define TransposeType as TransposeType:StorageView. However, this cyclic reference is not allowed in Swift.

public class StorageRowView<S:Storage>: StorageView {
    public typealias StorageType = S
    public typealias TransposeType = StorageColumnView<S>

    public var storage:StorageType
    public var window:[Range<Int>]

    public required init(storage:StorageType, window:[Range<Int>]) {
        self.storage = storage
        self.window = window
    }

    public func transpose() -> TransposeType {
        return TransposeType(storage: storage, window: [window[1], window[0]])
    }
}

public class StorageColumnView<S:Storage>: StorageView {
    public typealias StorageType = S
    public typealias TransposeType = StorageColumnView<S>

    public var storage:StorageType
    public var window:[Range<Int>]

    public required init(storage:StorageType, window:[Range<Int>]) {
        self.storage = storage
        self.window = window
    }

    public func transpose() -> TransposeType {
        return TransposeType(storage: storage, window: [window[1], window[0]])
    }
}

class Matrix<ViewType:StorageView> {
    typealias StorageType = ViewType.StorageType
    typealias TransposeType = ViewType.TransposeType

    var view:ViewType

    init(rows: Int, cols: Int) {
        let storage = StorageType(size: rows*cols)
        view = ViewType(storage: storage, window: [0..<rows, 0..<cols])
    }

    init(view:ViewType) {
        self.view = view
    }

    // this is an error because TransposeType is not defined
    // as being of type StorageView
    public func transpose() -> Matrix<TransposeType> {
        return Matrix<TransposeType>(view: view.transpose())
    }
}

The issue is that I can't write Matrix<TransposeType> because the Swift compiler doesn't know that TransposeType is actually of type StorageView. I've been trying to figure out if there was a way to tell the compiler later on that TransposeType is actually a StorageView, but without any luck. Any ideas on how to get around this problem while keeping the general structure (specifically, having separate views, storage, and matrices is important)?

Also, it's a valid point to ask why Matrix's type should be dependent on a view in the first place. That is related to a separate question I have (Design pattern for Swift using a protocol as a delegate).

Aucun commentaire:

Enregistrer un commentaire