<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
(local-set-key [(meta return)]
(ilam (shell-command-on-region (point) (mark) "perl -MMDK::Common")))
-->
<head>
<title>perl-MDK-Common tutorial</title>
<link rev="made" href="mailto:pixel at mandriva dot com" />
<meta name="keywords" content="perl mandriva pixel library functional" />
</head>
<body bgcolor="#FFFFFF" text="#000000" link="#0000ee" vlink="#551a8b">
<h1 align="center">perl-MDK-Common tutorial v0.1</h1>
<p>Guillaume Cottenceau (maintainer: <a href="mailto:pixel at mandriva dot com">Pixel</a>)</p>
<hr />
<h2>Introduction</h2>
<p>This document aims at helping people interested in learning
more on <tt>perl-MDK-Common</tt>, a Perl library which is intensively
used in Mandriva in-house software development.</p>
<p>The library adds some convenient "basic" functions to Perl,
allows easier functional-style programming, and also provides
some better system-related operations. It can be seen as an
extension to the standard Perl library, adding missing helpful
functions. It is divided as follows:</p>
<ul>
<li><tt>MDK::Common::File</tt>: some useful list/hash functions</li>
<li><tt>MDK::Common::Func</tt>: functions suited to functional-style programming</li>
<li><tt>MDK::Common::Math</tt>: some math functions</li>
<li><tt>MDK::Common::String</tt>: functions to perform various formatting on strings</li>
<li><tt>MDK::Common::System</tt>: system-related useful functions</li>
<li><tt>MDK::Common::Various</tt>: other useful functions</li>
</ul>
<p>Thanks to <tt>perl-MDK-Common</tt>'s own documentation, an
easy way to directly access information about the provided
functions is to use <tt>perldoc</tt>. For example,
<tt>perldoc MDK::Common::Func</tt> will list the functions
of the Func sub-module. Use <tt>perldoc MDK::Common</tt> to view
information on all the available functions.</p>
<p>Additionally, <tt>perl-MDK-Common</tt> provides a binary
called <tt>perl_checker</tt>, which is a Perl compiler aiming
at enforcing the use of a subset of Perl, so that all
Mandriva Perl programs roughly follow the same code style.
It will also help the programmer to remove unneeded parentheses
and conditionals.</p>
<hr />
<h2>Prerequisites</h2>
<p>Of course, a first look at the Perl language will be
necessary for the reader. The following can be a good Perl
Tutorial (although there are many others on the web): <a
href="http://www.comp.leeds.ac.uk/Perl/">http://www.comp.leeds.ac.uk/Perl/</a>.</p>
<p>Programming with <tt>perl-MDK-Common</tt> also emphasizes
the following quality properties on your code:</p>
<ul>
<li><b>no code duplication:</b> at the grassroots, this library
aims at helping you with simple operations you have to deal
with so many times in usual programs; this includes reading the
contents of a file, finding the maximum numeric element of an
array, etc; in order to be efficient with
<tt>perl-MDK-Common</tt>, you need to always keep in mind to
not duplicate code to perform a single action</li>
<br>
<li><b>functional style programming:</b> this is not a so
common technique among programmers, and maybe it's even worse
with Perl programmers; but functional-style programs are often
clearer, more expressive, more reusable, and more
maintainable, than traditional programs</li>
<br>
<li><b>strict code style:</b> Perl is known to be a language
with which "there is more than one way to do it"; actually,
nearly every Perl program uses a different code-style; that's
nice for the programmer's freedom, and that's awful for code
maintainance; <tt>perl_checker</tt> will ask you to follow a
specific code style</li>
</ul>
<p>We can't discuss Perl programming without referring to two
excellent books from O'Reilly. The first one is called "The
Perl Cookbook", and covers many daily problems a Perl
programmer will face, in a recipe-like fashion. All Perl
programmers should own this book :). The second one can be a
good resource for more skillful programmers, and is called
"Advanced Perl Programming"; it covers interesting advanced
features of Perl.</p>
<hr />
<h2>Structure of this document</h2>
<p>This document will first try to emphasize the most useful
functions of the <tt>perl-MDK-Common</tt> library, e.g. the
most commonly used and simple. Then, some functions whose use
is not trivial will be explained. As a last part, an
introduction to the code-style to please
<tt>perl_checker</tt> will be shown.</p>
<hr />
<h2>Most useful functions</h2>
<p><b>Note:</b> many functions' name, extending capabilities of
existing functions, or being their functional counterpart, are
suffixed with the underscore character (<tt>_</tt>); for
example, <tt>chomp_</tt> is the semantical equivalent of
<tt>chomp</tt>, but returns the chomp'ed results instead of
modifying its argument.</p>
<ul>
<li>
<b>cat_</b>(FILENAME): returns the file contents; in scalar
context it returns a single string, in array context it
returns the lines. If the file doesn't exist, it returns
<tt>undef</tt>.
<p>Perl IO operations are verbose and the API is cluttered.
There are many situations in which you want to read the
contents of a file, put it in a scalar or loop around the
files. <tt>cat_</tt> allows to do that easily:</p>
<pre class="SCREEN">
printf "Mandriva release:\n%s\n", cat_('/etc/mandriva-release');
foreach (cat_('/proc/mounts')) {
my ($dev, $where, $type) = split;
print "$dev is mounted on $where (type $type)\n";
}
</pre>
</li>
<li>
<b>output</b>(FILENAME, LIST): creates a file and outputs the
list (if the file exists, it is clobbered)
<p>Counterpart of <tt>cat_</tt>:</p>
<pre class="SCREEN">
output('/tmp/resolv.conf',
"search $domain\n",
map { "nameserver $_\n" } @name_servers);
</pre>
</li>
<li>
<b>member</b>(SCALAR, LIST): is the value in the list?
<p>Returns true if the value is stringwise equivalent to an
element of the list:</p>
<pre class="SCREEN">
if (!member($driver, @modules)) {
print "Sorry, the driver is not available in our modules.\n"
}
</pre>
</li>
<li>
<b>difference2</b>(ARRAY REF, ARRAY REF): returns the first
list without the element of the second list
<p>Performs a set-wise substraction, e.g. removes in first list
the elements that are members of the second one:</p>
<pre class="SCREEN">
my @types = difference2(\@available_types, \@bad_types);
print "Please select a type from: @types\n";
</pre>
</li>
<li>
<b>uniq</b>(LIST): returns the list with no duplicates
<p>Removes duplicates from the list, keeping the order of the
list, and the first element when duplicates.</p>
<pre class="SCREEN">
my @types = uniq map { (split)[2] } cat_('/proc/mounts');
print "Filesystem types in use: @types\n"
</pre>
</li>
<li>
<b>min</b>(LIST): returns the minimum number from a list
<p></p>
</li>
<li>
<b>max</b>(LIST): returns the maximum number from a list
<p></p>
</li>
<li>
<b>chomp_</b>(STRING): non-mutable version of <tt>chomp</tt>:
do not modify the argument, returns the chomp'ed value.
<p>Very useful for simple functional expressions.</p>
<p>Note: also works on lists: <tt>chomp_($a, $b)</tt> is
equivalent to <tt>chomp($a); chomp($b); ($a,$b)</tt>.</p>
<pre class="SCREEN">
my $pid = chomp_(cat_('/var/run/cardmgr.pid'));
</pre>
</li>
</ul>
<hr />
<h2>Other interesting functions</h2>
<p>The following describes functions whose use is not
trivial.</p>
<ul>
<li>
<b>if_</b>(BOOL, LIST)
<p>Returns LIST if the BOOL condition is true, else an empty
list.</p>
<p>Note: it's equivalent as doing <tt>BOOL ? LIST : ()</tt>,
except that since it's a function, LIST is evaluated even if
BOOL is false. It's useful because it's shorter and more
readable than the ternary <tt>?:</tt>.</p>
<p>A first convenient use is when you want to loop on a list
and conditionally on another:</p>
<pre class="SCREEN">
foreach (@types, if_($arch eq 'ppc', @ppc_types)) {
# ...
}</pre>
<p>It's also useful to select elements from a list and modify
them on-the-fly, e.g. performing the equivalent of a
<tt>grep</tt> then a <tt>map</tt>. It works because
Perl automatically concatenates lists.</p>
<pre class="SCREEN">
my @md_devices = map { if_(/^(md\d+)/, $1) } cat_('/proc/mdstat');
# equivalent (but much more elegant!) to:
my @md_devices = map { /^(md\d+)/ } grep { /^md\d+/ } cat_('/proc/mdstat');
</pre>
</li>
<li>
<b>substInFile</b> { CODE } FILENAME: executes the code for
each line of the file; you can know the end of the file is
reached using <tt>eof</tt>
<p>Typically used to change a parameter in a file:
<pre class="SCREEN">
substInFile { s/^FORWARD_IPV4.*\n//; $_ .= "FORWARD_IPV4=true\n" if eof } '/etc/sysconfig/network';
</pre>
<li>
<b>each_index</b> { CODE } LIST: iterate on a list to execute
some code needing the index of the list element (available in
<tt>$::i</tt>)
<p>Useful when you need to perform an action on each element of
a list, but you also need the index of each element:</p>
<pre class="SCREEN">
each_index { printf "%s mountpoint: $_", $::i == 2 ? 'third' : 'other' } cat_('/proc/mounts');
</pre>
</li>
</ul>
<hr />
<h2>perl_checker</h2>
<p>Let's examine now the code-style perl_checker wants you to
adopt. Let's consider the following naive code example:</p>
<pre class="SCREEN">
1: sub calc {
2: my ($x,$y) = @_;
3: $_ = $y;
4: ($x==0 && $y==0) and return -1;
5: my @tab = (1, 2, 3);
6:
7: /sysconfig\/i18n/ and return 1;
8: }
</pre>
<p>
The following problems are reported:
</p>
<ul>
<li><pre class="SCREEN">
line 2, character 12-12
you should have a space here</pre>
<p>
Good: <tt>my ($x, $y) = @_;</tt>
<br>
Why: you should put a space after the comma when specifying a list.
</p>
<li><pre class="SCREEN">
line 3, character 5-7
undeclared variable $_</pre>
<p>
Good: <tt>local $_ = $y;</tt>
<br>
Why: you should always localize <tt>$_</tt> when you set it, because it's a global variable.
</p>
<li><pre class="SCREEN">
line 4, character 8-8
you should have a space here</pre>
<p>
Good: <tt>($x == 0 && $y == 0) and return -1;</tt>
<br>
Why: you should put spaces before and after operators.
</p>
<li><pre class="SCREEN">
line 4, character 5-21
unneeded parentheses</pre>
<p>
Good: <tt>$x == 0 && $y == 0 and return -1;</tt>
<br>
Why: because of operators precedence, the parentheses are unneeded (if unsure about precedence, see <tt>perlop(1)</tt>)
</p>
<li><pre class="SCREEN">
line 5, character 8-12
unused variable @tab</pre>
<p>
Why: Assigning to unused variables is (typically) useless. If you really need to assign to an unused variable, prefix its name with `_' and perl_checker will stop boring you (for example, <tt>@_tab</tt>).
</p>
<li><pre class="SCREEN">
line 7, character 20-21
change the delimit character / to get rid of this escape</pre>
<p>
Good: <tt>m|sysconfig/i18n|</tt>
<br>
Why: <tt>/</tt> is not the only regexp delimiter! if you want to specify a slash in your regexp, use another delimiter so that your regexp will be more readable.
</p>
</ul>
<p>Finally, the correct code looks like:</p>
<pre class="SCREEN">
sub calc {
my ($x, $y) = @_;
local $_ = $y;
$x == 0 && $y == 0 and return -1;
my @_tab = (1, 2, 3);
m|sysconfig/i18n| and return 1;
}
</pre>
<p>Under Emacs, you might want to add the following to your
<tt>.emacs</tt> and then be able to validate your code with <tt>C-Enter</tt>:</p>
</p>
<pre class="SCREEN">
(defmacro ilam (&rest body) `(lambda () (interactive) ,@body))
(add-hook 'cperl-mode-hook
'(lambda ()
(local-set-key [(control return)]
(ilam (save-some-buffers 1) (compile (concat "perl_checker --restrict-to-files " (buffer-file-name (current-buffer))))))
))
</pre>
<hr />
<p>
Last update: Wed Apr 30 18:05:40 2003
</p>
</body>
</html>