This week's book giveaway is in the HTML Pages with CSS and JavaScript forum.
We're giving away four copies of Testing JavaScript Applications and have Lucas da Costa on-line!
See this thread for details.
Win a copy of Testing JavaScript Applications this week in the HTML Pages with CSS and JavaScript forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Bear Bibeault
  • Ron McLeod
  • Jeanne Boyarsky
  • Paul Clapham
Sheriffs:
  • Tim Cooke
  • Liutauras Vilda
  • Junilu Lacar
Saloon Keepers:
  • Tim Moores
  • Stephan van Hulst
  • Tim Holloway
  • fred rosenberger
  • salvin francis
Bartenders:
  • Piet Souris
  • Frits Walraven
  • Carey Brown

Designing for immutability: a Matrix class in Kotlin

 
Sheriff
Posts: 15813
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I've started another learning project for Kotlin at the JetBrains Academy (https://hyperskill.org), this time on matrix processing. They have you build up a program that can perform different kinds of matrix operations. So far I've done matrix sum, transpose, scalar multiplication, and matrix multiplication. Now working on calculating the determinant. However, I've started thinking about the overall design approach and I want to experiment with making my Matrix class immutable. The more I think about it the more it makes sense and it will give me a chance to get into a more functional-style of thinking about design.

I'll be adding to this thread as I go but will start off with matrix sum.

The first requirement for matrix sum is that the two matrices must have the same size. That is, they must have the same number of rows and the same number of columns, otherwise no matrix sum is possible. One of the first decisions to make is whether or not this operation produces a null when the sum operation can't be performed or if a RuntimeException should be thrown or something else (maybe return a Null Object matrix?). My current design returns null, making the signature like so:

This allows me to write this type of code:

Throwing a RuntimeException will complicate this a little bit.

Would love to hear thoughts on pros and cons of the different choices and what you might decide to do.

There are still other operations and many more decisions to talk about but we can start with this one for now. Thanks in advance.

(I know this doesn't have a whole lot to do with the question of immutability but it's one of the first decisions I made. We'll get to immutability in a bit)
 
Junilu Lacar
Sheriff
Posts: 15813
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I noted in my opening post that the decision on whether or not to return a nullable Matrix for a matrix sum operation didn't have a lot to do with the question of immutability. Then again, maybe it does. With immutable/nullable, you'd have these semantics:

The above seems reasonable to me. But then again, where's the nullability/mutability connection? I should probably get some breakfast before posting again.
 
Saloon Keeper
Posts: 12161
258
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Nullability has nothing to do with mutability. Case in point is the Haskell language, in which nothing is mutable, but it still provides the Maybe monad, which is like nullable types in other languages.

It really comes down to the following design choice: Do you expect the client to provide a matrix of the correct size, or do you check it for them and inform them of whether a sum exists?

The pros and cons are inherent in that question. Expecting a client to provide a matrix of the correct size requires them to know the size of the matrix. The pro is that they don't have to check that the sum exists afterwards. If you check the size of the matrix for them, they don't need to know the size beforehand, but they do need to check whether the sum exists.

There is no right answer. In Kotlin I would choose the option where you return a nullable matrix, because it's much easier to check the existence of the sum than it is to check that the matrix is the correct size, and the compiler even helps you remember. In Java I would have thrown an exception.

If you really want you can even add both methods and leave the choice to the client.
 
Junilu Lacar
Sheriff
Posts: 15813
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:Nullability has nothing to do with mutability.


Yes, that's what I kind of realized when I wrote those. I'm sure I'll circle back to this later because I remember running into a few minor annoyances as I was working on other learning projects. I don't remember specifics but I know there was a clash between something being null that I didn't want to be and doing something. I'll post details if I remember exactly what it was. Maybe it was null and declaring a variable with val vs. var. Anyway, it'll come back to me, hopefully.

Now, back to immutability/mutability... I think maybe the semantics and implementation for the matrix and scalar multiplication will provide better fodder for discussion. Will post some code and thoughts around it in a bit.
 
Junilu Lacar
Sheriff
Posts: 15813
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Looking around for what others have done, it seems like taking a clue from the standard Kotlin library wouldn't be a bad way to go. I think the general approach is to have both mutable and immutable versions and make the mutable versions extend the immutable bases. For example, a MutableMap instance can be assigned to a Map (immutable) but not vice versa.
 
Bartender
Posts: 4006
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
What would be the advantage of having mutable Matrices? In my Matrix class everything is immutable. One could argue that that might be costly when it comes to memory usage and perhaps performance might be an issue, but then again: the Matrix classes that we write on this site are just toys, not meant for any serious work. I have this method:
How many matrices are created?    
 
Junilu Lacar
Sheriff
Posts: 15813
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Let's say I want to start with designing an immutable Matrix then see how far I can get until I have to bite the bullet and add mutable semantics.

First, we start with instantiating and initializing Matrix. I had the following code that would be invalidated by an immutable Matrix design:

The original idea was to read values row-wise for as many rows as needed. How do I get around this? The first idea that came to mind was kind of along the lines as this guy's: https://github.com/yinpeng/kotlin-matrix where he has a few ways:

I'm not a big fan of the first method with the literals because of the way the dimensions are mixed in with the values. I kind of like the "by lambdas" and "from iterable" ones though.

Thoughts?
 
Rancher
Posts: 3571
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You could create a "setter" that returns a new Matrix with just that one element set.  Then do that M*N times to initialize an M*N matrix. ;)

No, I don't like that either.

More seriously, you could either (a) have a constructor or factory that initializes from an array of arrays, doing a defensive copy, or (b) use the builder pattern.  Make a mutable builder, and add the mutable method you need there, not on the immutable version.
 
Mike Simmons
Rancher
Posts: 3571
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Or support something like

 
Mike Simmons
Rancher
Posts: 3571
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hm, the "array of arrays" suggestion was made thinking of Java's array initializer expressions:

But Kotlin doesn't support that notation as concisely. Which leads back to the suggestion directly before this post, using method rather than array literals.
 
Junilu Lacar
Sheriff
Posts: 15813
264
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Design discussions sure are fun, aren't they?  

I kind of like this

Mike Simmons wrote:Or support something like


Maybe this?

This would be symmetrical to the Array<Array<...>> or arrayOf(arrayOf(...), arrayOf(...), ...)  declaration you'd do without the Matrix abstraction.
 
Stephan van Hulst
Saloon Keeper
Posts: 12161
258
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That implies that a matrix consists of matrices. It doesn't. It consists of rows and columns of elements.

Seeing as there is a thing called "irregular matrix", it's better to use row-major order (pass the elements in rows, instead of in columns). Finally, because matrices are often used in conjunction with vectors, you may create a Vector class and use that to model the rows.

Off topic, all this reminds me of how I prefer Haskell constructors. In Haskell I would write:
 
Mike Simmons
Rancher
Posts: 3571
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I agree with matrixOf and either rowOf or vectorOf, more consistent with existing Kotlin function names.  (And it's time to reclaim Vector from that crappy List progenitor that Java inflicted on the world.)  Though it reminds me I wish Kotlin had some good collection / array literals.  And a ternary operator. But I digress
 
Piet Souris
Bartender
Posts: 4006
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm not fond of this Vector thing. What is a Vector other than a 1xN Matrix or an Nx1 Matrix? How would you differ between the two?
 
Mike Simmons
Rancher
Posts: 3571
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
To me, that seems like asking if a line is a subtype of a square.  OK, that's not a perfect analogy, but the answer is still no.  A Vector (or Row) is always a one-dimensional collection of values; it has one length. A Matrix is always two-dimensional; it has a height and width.  It may be a Matix with a height of one, which is like a row.  But it's still conceptually a structure that could have had other rows, while a Vector is conceptually always a structure that has only one row.  Keeping this distinction allows us to say that a Matrix can be constructed from a list of Vectors, and we know that the result will have two dimensions.  If we have a Matrix constructed from a list of Matrices, then you could also construct higher-dimensional matrices as well - and they'd all have type Matrix.  How would you know how many indices to use to access an element?  You'd have to have a get(Vector) method to handle an arbitrarily long list of elements, except oh wait that's just a Matrix again, so now you have a Matrix.get(Matrix) method and I have no idea what that would even mean.  It's much easier to have a Matrix with 2 dimensions, period, and a Vector with one dimension, period.
 
Piet Souris
Bartender
Posts: 4006
156
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I take your 'period' as 'end of discussion'. Right.
 
Mike Simmons
Rancher
Posts: 3571
39
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sorry, I didn't mean it that way - rather as, I would prefer definitions that do not allow the dimensionality to be ambiguous.  I want the definition to say period, because I find that ambiguity here makes it harder to write useful methods or reason about what they are doing.  It's certainly possible to imagine a type with arbitrary dimensionality, but that seems unnecessarily confusing to me.

Also, I believe these definitions match those generally used in mathematics, from which we get these concepts.  So we minimize confusion by using them the same way.  But I don't assert that that's the only way to see things, period.
 
If you are using a wood chipper, you are doing it wrong. Even on this tiny ad:
Building a Better World in your Backyard by Paul Wheaton and Shawn Klassen-Koop
https://coderanch.com/wiki/718759/books/Building-World-Backyard-Paul-Wheaton
    Bookmark Topic Watch Topic
  • New Topic