The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
<!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>