Perl6::Math::Matrix (Part 2: Converter)

In this series of articles I reflect and expand on a talk I gave this year in Glasgow were I spoke about writing Perl 6 modules in general and my module in particular. Part I was about data types I used or wanted to use, because my approach is it to think first about data structures and built later the code around that. It is also crucial because this module basically provides the user with a new data type. (You might want to reread it - I expanded it to twice the size after publishing it.)

To continue where we left off, this part will be about converting our data type (Math::Matrix) into other types and we start best with the usual suspects. As I said in the intro of my talk: "your main job as a Perl 6 module author is not to screw up the inherit beauty of the internal design". It also makes your module much more usable if it provides meaningful information the often used contexts.


You might not use the prefix operator ? or its word like counterpart "so" as often, but an "if" (which also enforces the boolean context as its counterpart the ternary op or any other logical op) is the bedrock of programming. And since this module is obviously about math and all Numeric types (if asked about their boolean value) just check if the value is not zero, we should do the same. It is handy, that there is a thing called zero matrix (when all cells have value of 0). So in bool context I just deferred to the method .is-zero. This is another P6-convention we should not break: methods that return a Bool start with an "is-" .


This context is more tricky since it is enforced by a plus operator and others. I choose it to return the number of cells but I'm not entirely happy with it, since the size would be much more useful (a 22 and a 41 matrix are very different things so +$matrix1 == +?matrix2 would be rather misleading). On the other hand Numeric is about being a single value, so I'm between rock and a hard place on this one. A final insight is welcome.


This one is again easy, since everyone knows what to expect (I got it wrong the first time anyway - but the audience luckily caught me out). The rows are separated by new lines and the cells by spaces. This allows for nicely formatted matrices withing the source code by using heredocs. Conveniently .new also accepts this format as input, so we are also consistent there.


The main format .new expects is an Array of Arrays so I should have also an method that can return this format. Plus I implemented the AT-POS method, which allows this syntax $matrix[2][3]. The method itself returns of course just the array with the values of a row.


This is more of a toy, but when we have the content as an Array, why not have an Hash and work with these subscripts. Since Perl is much about data munging some might even need it to work efficiently on their data, because it is important to me to not make the format opaque. You might just use one or two math operations to transform your data and than reuse it elsewhere.


Returning a flat list (which is the expected) is no biggie, but there are several issues with it. You might want a row wise or column wise ordering, or even a list of lists. To fulfill all these needs I also implemented list-rows and list-column, which fulfills the last wish. list is just an alias to list-rows.flat, so if it is not what you want you can at least have list-columns.flat. Because lists are so useful, this method opens up the module to several worlds of functionalities. One of these are set operators. Does our matrix contain the value 1 ($matrix (cont) 1) or does it contain a value between one or three ($matrix (cont) 1..3) or are all values in the matrix within a set ((2..7) (cont) $matrix)? These are useful things to know and if you don't like this syntax,we also provide a .cont method ($matrix.cont(1 .. 3) ).

And surely your can map, grep, reduce and alike on the lists you get out the matrix content. As a shortcut - the module provides its own map, which maps over all cells and creates a new matrix. You could also reduce the rows (reduce-rows) or (reduce-columns) to create a list.


The method .Str is also called by print and put. And frankly it is embarrassing when your user does print $your-datatype and it return nothing or something that is not representative of the content of your object. Even more crucial and more used is say, which will call the method gist on your Object. The name gist has actually a meaning and for instance a list will be shortened (to not spam the shell) when you say $list. Accordingly say $matrix will show you a nicely formatted excerpt that fits on a standard terminal. But optional parameters allow you to adjust to any screen size. I'm also glad to announce the main feature of the newly released version 0.3.0 that gist now works with all Numeric types (including Bool and Complex) and it will be formatted in a way so that each column takes minimal space and complex numbers will stick out. I plan to provide third optional parameter for other formatting rules.

The output of gist will be cached, so after once you set your format, you can later just say $matrix instead of say $matrix.gist(...). (Don't worry you can change the format later by calling gist with different arguments).

And if you want to be very correct: provide 2 .gist multi methods. One for the defined object and one for the undefined value (type object), because any standard type object is also known by gist. (say Int gives you "(Int)").


Even if I provide both data formats accepted by new, I should also have a .perl method, because that is expected from me, the module author even more: to have a method which output can be eval-ed into a clone (for marshaling and other purposes).

Leave a comment

About lichtkind

user-pic Kephra, Articles, Books, Perl, Programming