|Do you know where your variables are?|
M alias MUMPS:
All jokes you could invent about naming a programming language after a viral disease can't even come close to how sick MUMPS is.
On the other hand, I see several features in MUMPS that were re-invented decades later. MUMPS has trees that can be used as multi-level associative arrays, and unlike Perl's hases, the associative arrays are even sorted. MUMPS has regular expressions, string eval, exception handling, locking, local, post-conditions, automatic converting from string to number and back, and some other features that you would not expect from a language designed to run on a PDP-7. The most important feature are "globals", structured variables available in all programs, stored on disk. MUMPS fans call that feature a database. Modern languages would perhaps call them persistent super-globals. Don't confuse them with ordinary global variables that are kept separately for each session in memory.
I see MUMPS as wild mix of Perl, DBM files, home computer BASIC, a macro assembler, and a big heap of punch cards. (Actually, MUMPS does not use punch cards, but "modern" VT420 terminals or VT420 emulations.)
The code below runs on MSM (Micronetics Standard M) Version 4.4.1. I think it could run on other MUMPS implementations with minor changes.
Note that this example uses quite modern constructs (for MUMPS), like user defined functions (returning a value) instead of procedures (returning no value, but possibly modifying global variables), arguments for functions (instead of using global variables), private variables (instead, as you may have guessed, using global variables). I just can't get used to stuffing everything into global variables. And my co-workers can't get used to using local variables. "We use global variables since three decades, and we never had problems with them." (Except for those "rare" cases once a month when global variables were accidentally overwritten.)
The code also is quite verbose for a MUMPS program. Most code I see at work has each and every line stuffed to the maximum allowed (because those ancient PDP machines executed code faster when it was stuffed into a single line), variable names tend to be as short as possible (why use all eight significant characters when you can use just two or three?), and comments are used as a poor replacement for SVN. I think my co-workers could write functionally equivalent code with half of the lines.
Explaining every aspect would take days, so I have to omit many details.
The program ROSETTA starts with the main routine, the first line contains the program name as a label and a comment, no executable code (by convention). Note that the timestamp is automatically updated by the editor (poor man's SVN). The second line assigns the three test cases to the string TESTDATA. The third line is a for loop, starting with I=1, incrementing I by 1 in every loop, with no upper limit. Inside the loop, R is set to the I-th piece of TESTDATA using the $PIECE function (pieces are separated with commas). Still inside the loop, the loop is aborted using the QUIT command if R is empty (because no more pieces are available), else the anonymous subroutine starting (and ending) in the next line is executed for each iteration. The fourth line writes the value of R, a colon and a space, the result of the ROM2DEC function invoked with the argument R, and a newline. The fifth line aborts the program.
The HELPER label defined a second function, it is the equivalent of the code block passed to List::Util::reduce(). Again, NEW is used to generate a new, temporary set of variables (so that the variables inside REDUCE are not overwritten), and as in REDUCE, the QUIT is used to return the value of an expression. Note that brackets are required due to the left-to-right evaluation order. # is the modulo operator.
The last function is ROM2DEC, with the usual prolog to generate a new, clean set of variables. RTOA is used as an associative array. Note that you have to assign each element separately, there is no shortcut notation. The longest line is the MUMPS equivalent to $x=~tr/[a-z]/[A-Z];, poor man's
And this is how you run ROSETTA from the programmer prompt:
You can also call the various functions inside the program:
Yes, MUMPS programs may have more than one enty point. There is no difference between a program and a library. You get used to that, like you get used to many other ugly tricks. Most notably that variables survive a program exit and are available for the next program.
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)