This action might not be possible to undo. Are you sure you want to continue?
While Modern::Perl cleaned up a few problems, it doesn't go nearly far enough, so perl5i set out to make Perl more or less totally modern. It was only after the fact that the complex interpretation, twisting Perl through 90 degrees, was suggested.
* When you set out to write a large system, you set out to write a large system. * At the other extreme are little snippets of code, which you run a few times. The next day you need a slight variant, then you need some additional processing. A few weeks later, a team member needs something similar, so you direct him to your existing code. * While the initial program is very simple, it's always good to do it cleanly, and as I add bits and pieces, I try to make it cleaner. * Unfortunately, starting with plain-old-perl means you are more likely to write a few lines performing some calculation manually, rather than including a module and using a debugged, reliable version. * So I have a standard template, that includes many of the things I like, to encourage me to DO THINGS RIGHT, from the beginning. For example, my template includes Getopt::Long, to resist the tempation to access $ARGV, and it has a process_args routine, so all I need do is ﬁll in the template and assign any default values. * When I started a new position, last winter, I decided to develop a new template which would be more up-todate, so decided to try perl5i. One advantage is that changes in perl5i will not break existing programs. Itʼs explicity versioned, with older versions included as a subset of each new version.
Modern::Perl gives you a few features: warnings, strict, features; Perl5i goes a lot further and includes English, to have meaningful system variable names, instead of all the punctuation variables. autodie means you donʼt have to “or die” on open, close, system calls, etc. Try::Tiny is a simple way to handle exceptions, without a whole lot of fuss. These on there own are enough to justify perl5i, in my opinion, not to mention it requires less typing. Remember: boilerplate is the root of all java. I hate the ﬁrst line of each program, where you split the arguments out of @_. perl5i gives you function and method keywords, which take argument lists, and handle that for you; method also automates handling self. You can also specify a different name, rather than self, by specifying it with a :sufﬁx. Autoboxing treats scalars, arrays, hashes, code as if they were objects, so you can invoke methods on them. This is a small stylistic point, on its own, but it allows the introduction of utility methods such as ltrim, rtrim, commify, and many others, without polluting the toplevel namespace. Child is a module that simpliﬁes forking off a block of code. CLASS replaces the __PACKAGE__ with $CLASS. File::chdir allows you to change directory lexically. File::stat returns the result of a stat query as an object. DateTime replaces time, gmtime and localtime with objects: time->year. Time::y2038 avoids the 2038 bug and the 1901 bug, works from year 1 to 247. IO::Handle turns ﬁlehandles, so you can call methods on them, most importantly autoﬂush(). autoviviﬁcation prevents autovivication, options to die or warn.
So I decided to write some short samples. I was also concerned that examples of code, in books, on web sites, encourage minimalist behaviour, rather than good software engineering. People learn by example. If everything you read has warnings and strict at the top, you take it for granted. Mattʼs Perl Archive encourages Perl4 styling. So I set out to do some Rosetta Code examples. If you had 100 doors, all closed, and you opened every one, then you closed every other one, then you toggled every third one, eventualy you would wind up with all the square-numbered doors, open ... or is it closed? That sounds like an object to my, you have a set of doors, and some methods that operate on the doors. I was avoiding Moose, so I did it with a traditional object package. The top-level doesnʼt show advantages of perl5i, other than minimized package boilerplate. Inside the package is different. Iʼve never liked the constant package, because the barewords are clumsy, and donʼt work in strings the way you want them to. I used to use Readonly, but that has some problems, in particular with performance. I had one program where I had a very large number of Readonly constant accesses, and I had to re-write it with ordinary variables, to get acceptable performance. Const::Fast is the fast solution. In the constructor method, we are going to create self; what we want in the arg list is the name of the class, so we specify an alternate name. Doing all the initialization in an _init method simpliﬁes inheritance.
In the initializer I keep track of how many doors we are ﬂipping, and initialize a set of doors. $CLOSED is 0, of course. Note that zero X a number returns the string, 000000000. The list (0) X a number returns a list, (0, 0, ...). Having this list in square brackets generates an anonymous array. Notice how pretty it is to not have a line my ($N) = @_. Toggling is handled by toggle_all, which delegates to toggle_n to toggle every Nth door, for every N in 1 ... N.
toggle_n goes through all the doors, delegating to toggle to handle a single door. If the arguement is 7, we want to toggle doors 7, 14, 21, ...84, 91, 98. By getting the integer dividend of N by $n, we get the upper limit, in this case 14. We iterate through the range 1..14, multiplying each number by $n to get the sequence we want, and then toggle the appropriate doors. and toggling a door is very simple.
Finally, printing which doors are open is a traversal of the list.
Of course the ordinary solution is shorter, but contains less information. While it might be a good initial solution, maintenance and extension is harder. I was satisﬁed by my solution, though others complained about verbosity.
But I wanted more Software Engineering points. So I took on the combinations challenge: given an alphabet a, b, c, ... , ﬁnd all combinations of a given length, listing the combinations in alphabetical order, with each combination element in alphabetical order. I decided the user should be able to test different situations, which required args processing, as well as POD documentation.
The solution to the challenge is recursive. First is a safety check, If the function is called with no alphabet, weʼve done the impossible and gone too far. If the function is requesting combinations of length 1, then each element of the alphabet is a candidate, all on its own. Otherwise weʼre looking for longer combinations. So there are two situations, one containing the ﬁrst element, and another excluding it. If itʼs included, peel off the head from the list, recursively get the now shorter length combinations, from the rest of the alphabet, and then stick the head back onto each combination. To exclude the ﬁrst element, determine the original length combinations from the rest of the alphabet. In parse_options, I set some default values, so invoking the program without arguments generates some result, then invoke GetOptions. If there are problems, or the user wants help, then Pod::Usage is invoked. Finally an alphabet is generated of the required length, and the option list returned. Once agin, the mainline is brief. parse the options, determine the combinations, and print the results, one per line.
and then thereʼs documentation
which we all hate to write
and which tends to get out of sync with the program.
Did I mention I donʼt like writing POD, or even routine documentation? and writing a parse_options routine for a routine in a book or web site is boring and repetitious. Far more useful to write tests. Tests demonstrate how the routine should be invoked, with what arguments. They explicitly show what return values or structures are expected. They show a number of invocations, covering the range of edge situations, as well as some ordinary situations. So here we are again. This time Iʼve made the ﬁle Combine.pm. The documentation is shorter, but the routine is unchanged.
If runtests is invoked, we load Test:;More, if not, it is skipped, since itʼs in an eval. Then we run the various indiviidual tests, and announce weʼre done testing. In case you havenʼt been paying attention, you no longer need to have a plan. By not dying prior to done_testing, we indicate success, and Test::;More handles the counting. Test for no alphabet, or for requesting zero-length sets
test one-element combinations of minimal and lengthier alphabets. and then the inductive step
given combinations of length N, demonstrate combinations of length N+1. run tests is only invoked if there is no caller(). In other words, weʼre running perl Combine.pm. If Combine were being included from another ﬁle, caller would have a value and we wouldnʼt test, just return 1.
There were a couple of interesting blog articles about testing by chromatic or ovid, I forget which, where he could run a single test when speciﬁed, or the entire set otherwise. I used this is some stuff I was doing at work, and liked it, but then they shut down the Ontario ofﬁce, and I havenʼt recreated it since. Iʼd also like to mention a module by Ingy called perl5 which makes it easier to eliminate boilerplate in a personalized way, by writing a plugin module once, that lists all your favourites, with optional versions.