Generic Specialization in "init" functions

Hi,

I am constructing a generic struct type of matrix and trying to provide different options for initialization.

protocol MatrixContainer {
    associatedtype Element
    init(_ rows:UInt, _ columns:UInt, with defaultVal:Element)
    subscript(rowId:Int) -> [Element] { get }
    subscript(rowId:Int, colId:Int) -> Element { get }
}

And a struct conforming to the MatrixContainer protocol

public struct Matrix<Element: Numeric & Comparable > : MatrixContainer {
    var mat: Array<Array<Element>>
    ...
    public init(_ rows:UInt, _ columns:UInt, with defaultVal:Element) {
        self.rows = rows
        self.columns = columns
        self.mat = Array.init(repeating: Array.init(repeating: defaultVal, count: Int(columns)), count: Int(rows))
    }
.....

Now I want the default initialization for Int, Double, Float and UInt I am specializing as follows.

extension Matrix where Element == Double {
    public init(_ rows:UInt, _ columns:UInt) {
        self.init(rows, columns, with: 0.0)
    }
}

extension Matrix where Element == Float {
    public init(_ rows:UInt, _ columns:UInt) {
        self.init(rows, columns, with: 0.0)
    }
}

extension Matrix where Element == Int {
    public init(_ rows:UInt, _ columns:UInt) {
        self.init(rows, columns, with: 0)
    }
}

extension Matrix where Element == UInt {
    public init(_ rows:UInt, _ columns:UInt) {
        self.init(rows, columns, with: 0)
    }
}

This works. But I'm not happy with the way I am generalizing it. I feel it is too verbose.

Is there a better way to specialize the init functions with cleaner and nicer syntax?

You can find the complete code here.

Double, Float, Int, and UInt all conform to Numeric. You can probably use that as a common ground.

@Lantua Thanks for the response. Yes,

As struct conforms to Numeric protocol. Problem is with the

defalutVal:Element the reason I'm trying to specialize is because of this constraint I'm forcing. Any sudo code example or some external source repo pointers will be of more help

Hmm, didn't see that. Since Matrix.Element already conforms to Numeric, you can just add it:

public init(_ rows:UInt, _ columns:UInt) {
  self.init(rows, columns, with: 0)
}

As a side note. I strongly suggest that you use Int for rows and columns instead. Likely you won't overflow, and it's easier to interface with other computations.

Also, I think this signature might be more inlined with the API Design Guidelines.

init(rows: Int, columns: Int, repeating: Element)
subscript(row row: Int)
subscript(row row: Int, column column: Int)

I have already tried this option. This gives the following error.

 error: cannot convert value of type 'Double' to expected argument type 'Element'
        self.init(rows, columns, with: 0.0)

That is why I have to specialize the extension with where clause. extension Matrix where Element == Double

Try 0 instead. There's a distinction between integer literal and floating point literal. And Numeric only refines ExpressibleByIntegerLiteral.

Also, even the subscript uses Int for rows and columns, I'd strongly suggest that you use Int.

1 Like

Numeric provides the .zero static property to get the zero element. Have you tried using:

public init(_ rows: UInt, _ columns: UInt) {
  self.init(rows, columns, with: .zero)
}
1 Like

Thank you. This works!

Yep, Making it as Int works aswel.

I will consider your suggestions and correct the same. Thanks a lot for your quick inputs and support.

@xAlien95 Can you please let me know where to find such info. I work on Linux and have limited code completion options in VSCode on Linux. Any tit bits on how and where to find such info will be useful to know.

You can look for the Numeric protocol in the Swift documentation, then under Relationships, Inerhits From there's the AdditiveArithmetic protocol, which requires the .zero static property. Without code completion you need to traverse all the parent protocols.

1 Like

Fixed in this commit.

1 Like