summaryrefslogtreecommitdiffstats
path: root/tutorial.html
blob: 7659949c10371a1d531531b68968e03376d5424e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
<!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>