Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift generics: requiring addition and multiplication abilities of a type

Tags:

swift

I'm trying out some examples from the Swift book, namely the matrix example they have which introduces subscript options. This is the code I have:

struct Matrix<T> {
    let rows: Int, columns: Int
    var grid: T[]

    var description: String {
        return "\(grid)"
    }

    init(rows: Int, columns: Int, initialValue: T) {
        self.rows = rows
        self.columns = columns
        grid = Array(count: rows * columns, repeatedValue: initialValue)
    }

    func indexIsValidForRow(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }

    subscript(row: Int, column: Int) -> T {
        get {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

This is mostly copied from the book. A major difference is in this line here:

struct Matrix<T>

As far as I can tell, this says to the compiler that my Matrix class can hold values of type T, specified by the code using this class. Now, I'd like to make sure that the type T can be compared, so I can write this:

struct Matrix<T: Equatable>

This might be useful in case I want to compare 2 matrices, which would mean comparing their values. I also want to provide the ability to sum two matrices, so I should also add to this line a protocol requiring that the type 'T' given by the user of the matrix can be added:

struct Matrix<T: Equatable, "Summable">

Likewise, I'd also like to say:

struct Matrix<T: Equatable, "Summable", "Multipliable">

Question 1: What protocol name can I use? How can I achieve this?

On a related note, to add addition abilities using the '+' operator, I should declare a function like this (this applies also to multiplication):

@infix func + (m1: Matrix<T>, m2: Matrix<T>) -> Matrix<T> {
    // perform addition here and return a new matrix
    return result
}

However, this code is not accepted by Xcode. More specifically, this ) -> Matrix<T> { produces the error: Use of undeclared type 'T'. What I mean by that <T> is that the result will be a matrix that has the same type of the two input matrices, but I'm probably messing the syntax completely.

Question 2: How can I provide type information to the result of the addition?

like image 863
Alex Avatar asked Jun 04 '14 20:06

Alex


1 Answers

Here's for your second question (but you really should ask two separate questions):

@infix func + <T> (m1: Matrix<T>, m2: Matrix<T>) -> Matrix<T> { ... }

For your first question: before solving it, here's the syntax to define multiple constraints for type parameter:

struct Matrix<T where T: Equatable, T: Summable, T: Multipliable> {...}

or, as GoZoner writes in the comments:

struct Matrix<T: protocol<Equatable, Summable, Multipliable>> {...}

But we're not going to need it. First, define a new protocol and list the operations that you need. You can even make it extend Equatable:

protocol SummableMultipliable: Equatable {
    func +(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
}

Then, provide extensions for the types that you want to conform. Here, for Int and Double, the extensions are even empty, as the implementation of the needed ops is built-in:

extension Int: SummableMultipliable {}
extension Double: SummableMultipliable {}

Then, declare your type constraint on the type parameter:

struct Matrix<T: SummableMultipliable> { ... }

Finally, you can write stuff like this:

let intMat = Matrix<Int>(rows: 3, columns: 3, initialValue: 0)
let doubleMat = Matrix<Double>(rows: 3, columns: 3, initialValue: 0)
let i: Int = intMat[0,0]
let d: Double = doubleMat[0,0]

The last thing you'll need is to insert the type constraint in the definition of your operator:

@infix func + <T: SummableMultipliable> (m1: Matrix<T>, m2: Matrix<T>) -> Matrix<T> { ... }
like image 124
Jean-Philippe Pellet Avatar answered Sep 27 '22 19:09

Jean-Philippe Pellet