Perl6::Math::Matrix (Part 4: naming methods)
While reflecting on how to write a good Perl 6 module, I thought a lot about how to properly name my methods. In this text I want to summarize what served me, which is a direct continuation of the last part, where I wrote about when it is helpful that methods share one name.
Do (not) Dare (to) Differ
In software engineering exists a discipline called DDD, in long Domain Driven Design, which is just an academic term for: "just use the language people of a certain field are used to". That just makes sense for effective communication, but why? (If psychology bores you, just skip the next two paragraphs.)
If your interested, look into the works of neurologists António Damásio and others. He found, that only with emotions attached information get remembered at all. And to digest logical principles and connections and braiding them into the knowledge web of your long term memory is actually demanding labor. No wonder the information get soaked with personal emotions while doing that. And even less wonder people are not keen to do that over and over and stick to a once learned meaning of a word. It will cause constant friction, if you insist using the same word in different way or a different word for same meaning, although latter is less irritating.
The crucial part of this mechanism is: while reading about it, most people recognize, that something of that nature happens, but very few recognize it while its taking place. Their are just occupied with the content of their thinking because the rational mind provably can handle only a chosen few information at the same time. Meanwhile we use a more emotional part of the brain to navigate the wider net of remembered information.
That is why using your module will be a much more pleasant experience if names of methods, packages and types are intuitive understandable or at least lead you into the right direction. Many jingles and slogans in our industry demand more intuitive API, but there is a rational thinking process that helps you to craft somethings that in the end will appear self-evident.
Math(ematica)
Since the package Math::Matrix is blatantly about mathematics we should use its language. Words like norm, determinant, identity, kernel, minor and many more are just borrowed from there to not confuse mathematicians or mislead programmer, that later might reach for literature to know more. But even mathematics has its history and culture and for instance an adjugate matrix can also be called classical adjoint or sometimes adjunct. In that case I went with adjugate because it's a recently used term, it's short and I sensed the least potential for ambiguity with other existing methods.
Misunderstandings can also reduced by naming the parameter properly. In part 3 for instance I mentioned that we have method $matrix.submatrix($m, $n) that works as described by your text book. Additionally the multi method provides the syntax $matrix.submatrix( rows => (1..3), columns => (0..4)) as some sources recognize that also as a submatrix. The existence of the arguments increase the distinction between the use cases, and their denomination makes not only the second case clearer but also a third, which is more complex: $matrix.submatrix( rows => (3,1,2), columns => (0,1,4)) (get from top to bottom the fourth, second and third row and from left to right the first, second and fifth column of the original matrix).
The second case also hinted on my suggestion to consider the cultural environment of mathematicians (for instance popular software like mathematica or octave). The additional constructors mentioned in the third part were certainly inspired by such tools and aimed to service academics with the workflow their used to.
Perl 6 makes it very easy to create custom operators and using Unicode for that is a none issue. That is why Math::Matrix also imports operators for much of the mathematical ops. To the trained eye ‖ $matrix ‖ is just natural and more intuitive to understand that $matrix.norm. To be mindful of people with less easy access to Unicode there are alternatives as +$matrix as well. Even that was was a happy coincidence, since prefix + in Perl 6 signifies the (single value) numeric context and this is the exact mathematical definition of a norm. However - the prefix - is in Perl 6 expected to be the exact same thing, just negated. This means trouble, since on the one hand, a negated norm is like the negated size of an object - you almost never want to know that (and you still can get it with -+$matrix). And on the other hand it is very useful to negate all cell values, when doing Gaussian matrix manipulation (which is then also very intuitively to read for mathematician).
To summarize: I made a deal here to maximize usefulness. This might upset some purists, but operators are syntactic sugar anyway. When it comes to the methods of basic functionality I would be much stricter.
Perl 6
The other culture informing our API is naturally the language we use: Perl 6. This shows in many details as for instance in writing is-zero instead of is_zero (as we would in Perl 5). The dash is much better at signalling "this is one combined name" and common place in the core. It was not an option in Perl 5, where we had to use the under_score or CamelCase (still used for module names). A standard that still holds is to use lower case for ordinary methods (uppercase signifies danger). But at one occasion I broke this rule, when choosing T as a shortcut for creating a transposed matrix. It looks more like an Operator anyway (just on letter) and was a too well established symbol in math anyway.
Value vs Container
Perl 6 has data types (Int, Str, Bool, ..etc.) and container types (Scalar, Array, Hash), that hold the former. The data objects are immutable to get all sorts of goodies as functional languages have discovered. In part one I wrote, that it was one basic decision to see a matrix as a data type i.e. immutable. Our documentations says so clearly but I think it is also a good idea to craft method names cautiously so that the user will get that intuitively. This is the reason why the long form of T spells transposed and not transpose. You will create a transposed matrix, but the original will stay unchanged.
lists and arrays
Sometimes you want to prepend, append or insert some rows or columns or even remove parts - sounds like a lot of methods to bloat our already very rich API. This is why I used a metaphor, well known to every Perl 5 and Perl 6 programmer called splice. It is the Swiss army knife to make all kinds changes to a list: push, pop, shift, insert and remove are just special cases of what this command is capable of. So I implemented splice-rows and splice-columns which work exactly like expected, in the one dimension of the table, the new commands are named after.
With map I went even a step further, because it is such a corner stone of Perl programming. Usually map takes a list (we prefer to say array), transforms every element via an anonymous block and returns a list with the results. Nothing stops you from writing $resultlist = $matrix.list.map: {...};. But often you want to preserve the structure of the matrix and get a new one, just made of the results. That I called boldly map too, since it maps over each cell and (as the original) returns the same collective type as you put in and this way using the metaphor of map a little looser. As any normal Perl-programmer, you expect the current cell value inside the anonymous block to be $_, even you can nicely call it $^anyname in Perl 6. But I found, that for some transformations of matrices you also need the current row and column index of the cell while doing a map. But this would be a too big departure from the original metaphor. This difference has to to be sufficiently visible, as I named it map-with-index.
conclusio (latin for TL;DR)
To create an enjoyable API draw on familiar and established names, naming standards, workflows and metaphors. That might even outweigh in rare cases consistency - but never sacrifice necessary innovation.
very good advice and insights - some questions: is each element a container? can it be a matrix of objects?
Thank You Steve you like it,
Its more detailed in part one (follow links to previous parts at start). Each cell has to be Number (Numeric type) and is readonly.