<?xml version="1.0" encoding="UTF-8"?><!-- generator="wordpress.com" -->
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>extlib &amp;laquo; WordPress.com Tag Feed</title>
	<link>http://en.wordpress.com/tag/extlib/</link>
	<description>Feed of posts on WordPress.com tagged "extlib"</description>
	<pubDate>Sun, 06 Dec 2009 10:50:34 +0000</pubDate>

	<generator>http://en.wordpress.com/tags/</generator>
	<language>en</language>

<item>
<title><![CDATA[Ronin "faster" 0.2.3 has been released]]></title>
<link>http://houseofpostmodern.wordpress.com/2009/05/07/ronin-faster-023-has-been-released/</link>
<pubDate>Thu, 07 May 2009 04:21:43 +0000</pubDate>
<dc:creator>postmodern3</dc:creator>
<guid>http://houseofpostmodern.wordpress.com/2009/05/07/ronin-faster-023-has-been-released/</guid>
<description><![CDATA[The wait is over, Ronin 0.2.3 (code-named &#8220;faster&#8221;) has finally been released. This rele]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>The wait is over, Ronin 0.2.3 (code-named &#8220;faster&#8221;) has finally been released. This release contains new code, more specs, some very important architectural changes and a few bug-fixes.</p>
<h2>Ronin on Ruby 1.9.1</h2>
<p>Probably the most important news in Ronin 0.2.3, is that Ronin is now Ruby 1.9.1 compatible. Ronin can now take advantage of the considerable performance improvements in Ruby 1.9.1-p0. If you tend to do security research and find yourself having to use Ruby 1.9.1, you should look into using Ronin.</p>
<h2>Faster Load-Times</h2>
<p>Ronin also saw various architectural changes to help reduce load-times. The <kbd>ronin/models.rb</kbd> file was removed, which loaded models from the other Ronin libraries before the Database was setup. Now other Ronin libraries can call the <a href="http://ronin.rubyforge.org/docs/ronin/Ronin/Database.html#update%21-class_method">Database.update!</a> method, which will run non-destructive auto-migrations on the Database. <a href="http://ronin.rubyforge.org/docs/ronin/Ronin/UI/CommandLine.html">Ronin::UI::CommandLine</a> saw yet more refactoring. With the new <a href="http://ronin.rubyforge.org/docs/ronin/Ronin/UI/CommandLine.html">Ronin::UI::CommandLine</a>, sub-commands are loaded on-demand, instead of all at once.</p>
<p>Together these architectural changes have dramatically improved the load-time of Ronin&#8217;s console. On systems that rarely run the Ruby interpreter, start-up times for Ronin should look like the following:</p>
<pre>$ time (echo exit &#124; (ronin &#62; /dev/null))

real	0m3.841s
user	0m1.141s
sys	0m0.514s</pre>
<p>On systems that regularly run the Ruby interpreter (thus caching frequently used memory and data) start-up times for Ronin will be a little quicker:</p>
<pre>$ time (echo exit &#124; (ronin &#62; /dev/null))

real	0m1.656s
user	0m1.137s
sys	0m0.478s</pre>
<h2>New Convenience Methods</h2>
<p>In 0.2.3 the <a href="http://ronin.rubyforge.org/docs/ronin/IPAddr.html#each-instance_method">IPAddr#each</a> and <a href="http://ronin.rubyforge.org/docs/ronin/IPAddr.html#each-class_method">IPAddr.each</a> methods were added. It&#8217;s somewhat common to need to iterate over a range of IP addresses. Say you have a CIDR notation IP address, and need to iterate over every IP address covered by it&#8217;s netmask. Simply create a new <kbd>IPAddr</kbd> object and call <kbd>each</kbd>:</p>
<pre>ip = IPAddr.new('10.1.1.1/24')
ip.each do &#124;addr&#124;
  ...
end</pre>
<p>Well that&#8217;s sort of cool, but what if you have a globbed IP address, similar to the ones <a href="http://www.insecure.org/">nmap</a> accepts? <a href="http://ronin.rubyforge.org/docs/ronin/IPAddr.html#each-class_method">IPAddr.each</a> has you covered:</p>
<pre>IPAddr.each('10.1.1-5.*') do &#124;addr&#124;
  ...
end</pre>
<p>Both <kbd>IPAddr#each</kbd> and <kbd>IPAddr.each</kbd> can  iterate over IPv6 addresses.</p>
<p><a href="http://ronin.rubyforge.org/docs/ronin/Net.html#http_powered_by-class_method">Net.http_powered_by</a> and <a href="http://ronin.rubyforge.org/docs/ronin/Net.html#http_server-class_method">Net.http_server</a> also got added in 0.2.3. These methods provide quick access to the <kbd>X-Powered-By</kbd> and <kbd>Server</kbd> HTTP headers, respectively.</p>
<pre>Net.http_powered_by(:url =&#62; 'http://www.stalkdaily.com/')
# =&#62; "PHP/5.2.9"

Net.http_server(:url =&#62; 'http://www.darkc0de.com/)
# =&#62; "Apache/2.2.11 (Unix) PHP/4.4.9 mod_ssl/2.2.11 OpenSSL/0.9.8c mod_fastcgi/2.4.6 Phusion_Passenger/2.1.2 DAV/2 SVN/1.4.2"</pre>
<p><a href="http://ronin.rubyforge.org/docs/ronin/String.html#pad-instance_method">String#pad</a> was also added in 0.2.3. The pad method doesn&#8217;t do a lot, it merely pads a String out to a maximum length:</p>
<pre>"hello".pad('A', 48)
# =&#62; "helloAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"</pre>
<p>If you hate having to deal with exceptions in Ruby, the <kbd>try</kbd> method might prove useful. The <kbd>try</kbd> method simple attempts to run a block of code, catching and ignoring any exceptions that were raised:</p>
<pre>require 'resolv'

try do
  Resolv.getaddress('might.not.exist.com')
end</pre>
<h2>Cacheable</h2>
<p><kbd>Ronin::Objectify</kbd> was replaced by the new <a href="http://ronin.rubyforge.org/docs/ronin/Ronin/Cacheable.html">Ronin::Cacheable</a> module. Cacheable provides reliable caching and loading of <a href="http://contextify.rubyforge.org/">Contextified</a> objects with Ronin&#8217;s Database. Using the new Cacheble module, the data you want cached into the database must be defined in a <kbd>cache</kbd> block:</p>
<pre>ronin_exploit do
  cache do
    self.name = 'stupidhttpd'
    self.version = '0.2'
    self.author(:name =&#62; 'Postmodern')
  end

  ...
end</pre>
<p>The use of a <kbd>cache</kbd> block creates a separation between the data to be cached and the code which will eventually be loaded.</p>
<h2>Overlays</h2>
<p>As of 0.2.3, overlays now support the automatic loading of the <kbd>lib/init.rb</kbd> file. So if you have code you&#8217;d like automatically loaded (maybe extensions to the Array class) from your Overlay, simply require it in the <kbd>lib/init.rb</kbd> file.</p>
<p>The loading of Extensions from Overlays became a little more robust in 0.2.3. If an exception is encountered when loading an extension file, the exception will be printed and the file ignored.</p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[OCaml Batteries Included, version alpha]]></title>
<link>http://dutherenverseauborddelatable.wordpress.com/2008/10/11/ocaml-batteries-included-version-alpha/</link>
<pubDate>Sat, 11 Oct 2008 13:35:42 +0000</pubDate>
<dc:creator>yoric</dc:creator>
<guid>http://dutherenverseauborddelatable.wordpress.com/2008/10/11/ocaml-batteries-included-version-alpha/</guid>
<description><![CDATA[It has landed The first alpha version of OCaml Batteries Included has landed. It is now available as]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><h1 style="text-align:justify;">It has landed</h1>
<p style="text-align:justify;">The first alpha version of OCaml Batteries Included has landed. It is now available as source code, <a href="https://forge.ocamlcore.org/frs/?group_id=17&#38;release_id=44">from the Forge</a> or as a <a href="http://godi.camlcity.org/godi/toc/toc-3.10.html">GODI</a> package. You may find the API documentation <a href="http://forge.ocamlcore.org/docman/index.php?group_id=17&#38;selected_doc_group_id=49&#38;language_id=1">here</a> and the complete documentation <a href="http://forge.ocamlcore.org/docman/index.php?group_id=17&#38;selected_doc_group_id=59&#38;language_id=1">there</a>.</p>
<p style="text-align:justify;">Remember, this is alpha-level code, use at your own risk. Also note that documentation generation is very slow (10+ minute on my laptop), so don&#8217;t worry if installation seems to last forever, it&#8217;s nearly true but not quite.</p>
<p style="text-align:justify;">Reviews, comments, suggestions and bug reports are particularly welcome. We have a few <a href="https://forge.ocamlcore.org/tracker/?group_id=17">trackers</a> for these, as well as <a href="https://forge.ocamlcore.org/forum/?group_id=17">a forum</a>, so don&#8217;t hesitate to use them. We&#8217;re also looking for volunteers to give us a hand, so please consider stepping forward.</p>
<h1 style="text-align:justify;">What is OCaml Batteries Included?</h1>
<p style="text-align:justify;">Twenty years ago, a language was just a compiler. You could measure the quality of a language from the beauty of its semantics, the clarity of its syntax, the speed of generated code. That was then.  In the meantime, the Java and .Net nuclear plants have been built, while the Python and Ruby communities have gradually developed their own powerhouses. All these platforms have amply demonstrated that a language can only be as beautiful, clear and fast as the libraries which developers are actually going to use for their work. In other words, it&#8217;s not about the language anymore, it&#8217;s about the platform.</p>
<p style="text-align:justify;">At this point in time, out-of-the-box, OCaml isn&#8217;t a usable platform. There is no Unicode, there are no modern user interface toolkits, no distributed programming infrastructures, no network services, no type-safe communications, no analysis of other languages, no interfacing with industrial platforms, no XML, non modern two- or three-dimensional drawing engine, etc. This would not be too much of a problem if OCaml provided an easy way of installing new libraries and of using these libraries once they are installed.  This would also not be too much of a problem if OCaml could somehow guarantee that trivial data structures didn&#8217;t need to be reinvented by most projects and that communication between libraries happened lawlessly.</p>
<p style="text-align:justify;">So no, out of the box, OCaml isn&#8217;t a usable platform. However, no matter what you&#8217;re trying to do, chances are that the community has already developed or adapted a tool to make your life easier. Easy installation of OCaml packages is quite possible, if you are using Debian or Ubuntu (apt-get), Fedora or Red Hat (yum) or actually, any Unix-compatible platform, including MacOS X and Windows (GODI). Simple and reliable usage of installed libraries can be done with Findlib.  A comprehensive Unicode library is available (Camomile), as well as a modern user interface toolkit or two (LablGtk, OCamlRT), a number of distributed programming infrastructures (OCamlMPI, BSML, Opis, Camlp3l, OCamlNAE), libraries to interface with industrial platforms<br />
(OCamlJava, SpiderCaml &#8230;), to analyse other languages (CIL, Dalton/FlowCaml, &#8230;), to read or write XML (PXP, Expat, &#8230;), to draw in two or three dimensions (Cairo, OpenGL), to test your programs (OUnit), etc. OCaml even offers a built-in tool to customize the language itself (Camlp4).</p>
<p style="text-align:justify;">What&#8217;s missing? A few things. For now, not all important libraries are available as simple-to-install packages. That problem is being addressed by the devoted packagers of Debian, Fedora and GODI, while the possibility exists that the recently announced Symbiosis will also help address the issue.  The other missing part is a standard set of libraries, language extensions and data structures which developers could be assured to find on every OCaml installation, and which would let them write programs without having to endlessly reinvent the same basic wheels, and without spending their time writing adapters between libraries which should work well together but don&#8217;t use the same conventions. OCaml Batteries Included is one possible answer to this problem.</p>
<p style="text-align:justify;">OCaml Batteries Included consists in</p>
<ul style="text-align:justify;">
<li>A core set of libraries, designed to define the basic standard data structures. This set, largely based on both the Base library of OCaml and ExtLib, extends the basic strings, arrays, lists&#8230; provided with OCaml and introduces numerous data structures, including enumerations, lazy lists, extendable inputs<br />
and outputs, dynamic arrays, unicode ropes&#8230;</li>
<li>A uniformization layer, the glue, on top of chosen existing libraries. Note that we are not forking any of these libraries, only providing an additional layer on top of them.  The libraries may be installed manually by the user or, preferably, by automatic dependency resolution thanks to apt-get, yum, GODI or some other packaging tool. The uniformization layer serves to guarantee that libraries play together nicely, that only one manner of reading from a file or writing to the output is necessary, that every data provided by a library may be decoded by another, etc.</li>
<li>Additional documentation on whichever library for which we provide glue, including both low-level documentation (&#8220;what the heck does this function do?&#8221;), mid-level documentation (&#8220;why should I use that function?&#8221;) and high-level documentation (&#8220;ok,I&#8217;m new here, where do I start?&#8221;)</li>
<li>A handful of language extensions, provided as Camlp4 modules, to solve common issues, automatically generate boilerplate code, improve readability, &#8230;</li>
<li>In the future, possibly a set of external tools.</li>
<li>Build tools to make all of this transparent.</li>
<li>And, of course, a logo.</li>
</ul>
<div style="text-align:justify;">
<div id="attachment_184" class="wp-caption alignnone" style="width: 449px"><img class="size-full wp-image-184" title="batteries_large" src="http://dutherenverseauborddelatable.wordpress.com/files/2008/10/batteries_large.png" alt="OCaml Batteries Included" width="439" height="300" /><p class="wp-caption-text">(Batteries courtesy of Benjamin Pavie, Camel courtesy of John Olsen, no animals injured) </p></div>
</div>
<p style="text-align:justify;">
<p style="text-align:justify;">OCaml Batteries Included is a project maintained by the community, which means that it depends on you. If you have ideas, suggestions, complaints, bug reports, if you want to participate, to write code, documentation, tutorials, build tools, review code, have a word in policies, if you want a package to be included in the Batteries, or just to contact us, please visit <a href="http://forge.ocamlcore.org/batteries">our website</a> and take advantage of our <a href="http://forge.ocamlcore.org/tracker/?atid=151&#38;group_id=17&#38;func=browse">bug trackers</a>, <a href="http://forge.ocamlcore.org/tracker/?atid=309&#38;group_id=17&#38;func=browse">request for features trackers</a>, <a href="http://forge.ocamlcore.org/forum/?group_id=17">forums</a>, etc. (courtesy of <a href="http://ocamlcore.org/">OCaml Core</a>). We can also often be seen <a href="irc://irc.freenode.net/#ocaml">on irc, on server freenode, channel #ocaml</a> and, of course on the <a href="http://caml.inria.fr/pub/ml-archives/caml-list/index.en.html">OCaml Mailing-List</a>.</p>
<p style="text-align:justify;">OCaml Batteries Included is also a work-in-progress. The version you have in front of your eyes does not contain everything we want to put in it, nor even all the code we have written for it. But we&#8217;ll get there.  We intend to integrate additional libraries progressively, from minor release to minor release, with major milestones approximately twice per year. This policy is not written in stone and is largely subject to debate, so don&#8217;t hesitate to comment on the subject.</p>
<p style="text-align:justify;">For more informations on the contents of OCaml Batteries Included, follow us towards <a href="api/index.html">the manual</a>.</p>
<h1 style="text-align:justify;">Relations to other libraries</h1>
<h2 style="text-align:justify;">Project Gallium&#8217;s Base Library</h2>
<p style="text-align:justify;">First, a word on vocabulary. We call &#8220;Base library&#8221; <a href="http://caml.inria.fr">the library provided by INRIA with the default distribution of OCaml</a>. We don&#8217;t call it &#8220;standard library&#8221; for the simple reason that there are several libraries vying for the status of standard, including Batteries Included.</p>
<p style="text-align:justify;">The relation between Batteries Included and the Base Library is simple: the Base Library is one of the libraries for which Batteries Included provides a uniformization layer. We are not forking the library, merely providing additional functions, additional documentation, boilerplate code&#8230;</p>
<p style="text-align:justify;">The complete Base Library is available in Batteries Included as module <code>Legacy</code>. Most modules are also available inside the Batteries module hierarchy, sometimes under different names, and usually completed by numerous new functions. A few modules are considered obsolete and appear only in <code>Legacy</code>.</p>
<h2 style="text-align:justify;">Jane Street&#8217;s Core</h2>
<p style="text-align:justify;"><a href="http://ocaml.janestcapital.com/">Jane Street&#8217;s Core</a> is another library vying for the status of standard, this time produced by Jane Street Capital. This library is comparable in purpose and design to the core of Batteries Included (actually, we draw some inspiration from them and we hope that they are going to draw some inspiration from us, too) but there is no code shared between Batteries Included and Jane Street&#8217;s Core for the moment.</p>
<p style="text-align:justify;">In the future, Batteries Included may depend on Jane Street&#8217;s Core. This is not the case yet as, according to one of Jane Street&#8217;s Core&#8217;s authors, this library may change quite a lot, and in what we understand as possibly incompatible ways, before reaching version 1.0.</p>
<p style="text-align:justify;">We already depend on two components of Jane Street&#8217;s Core: Type-Conv (a general infrastructure which may be used to generate boilerplate code) and Sexplib (an instance of Type-Conv used to generate code for serializing to/deserializing from human-readable S-Expressions). We intend to extend this to a third component of Jane Street&#8217;s Core: Bin-Prot, another instance of Type-Conv, used this time to generate serialization to/from binary protocols.</p>
<h2 style="text-align:justify;">ExtLib</h2>
<p style="text-align:justify;"><a href="http://code.google.com/p/ocaml-extlib/">ExtLib</a> is another extension of the OCaml Base Library. ExtLib was designed as a relatively small addition, with the idea of fitting nicely within the OCaml Base Library. Since 2005, ExtLib has essentially stopped growing, following a conscious choice from the maintainers.</p>
<p style="text-align:justify;">OCaml Batteries Included is largely based on ExtLib. Indeed, the core of Batteries is essentially a fork of ExtLib. We intend to maintain  ascending compatibility with ExtLib (with the exception of changes in module names), although we have added a great number of features,  fixed bugs, split code, etc. For most purposes, OCaml Batteries Included contains ExtLib and, in our mind, superseeds this library. We hope that the maintainers and developers of ExtLib will consider migrating to OCaml Batteries Included and helping us with their skills.</p>
<h2 style="text-align:justify;">Community OCaml</h2>
<p style="text-align:justify;"><a href="http://github.com/thelema/ocaml-community/tree/master">Community OCaml</a> is an attempt to turn OCaml into an out-of-the-box complete development platform, including numerous libraries.  We have joined forces with the developers of Community OCaml and we share most of our code. Although the design of Community OCaml is not completely decided yet, it is quite possible that this will end up as distribution consisting in OCaml + Batteries Included + some package manager.</p>
<h2 style="text-align:justify;">OCamlNet</h2>
<p style="text-align:justify;"><a href="http://projects.camlcity.org/projects/ocamlnet.html">OCamlNet</a> is an implementation of numerous Internet protocols for OCaml, along with so many utilities that it almost constitues a complete general-purpose development library for OCaml.</p>
<p style="text-align:justify;">OCaml Batteries Included will depend on OCamlNet and incorporate OCamlNet into its hierarchy. In particular, for the moment, OCaml Batteries Included provides little in the way of libraries for interacting with the operating system, as we intend to use OCamlNet for that purpose.</p>
<h2 style="text-align:justify;">Caml Development Kit</h2>
<p style="text-align:justify;">The <a href="http://pauillac.inria.fr/cdk/">Caml Development Kit</a>, or CDK, was another attempt to build a development platform around OCaml by bundling together OCaml itself and a number of interesting libraries and tools. By opposition to Batteries Included, this was a monolithic distribution, with a custom compiler and very little documentation. Development on CDK seems to have died around 2001.</p>
<p style="text-align:justify;">There is no relation between CDK and Batteries Included other than the common goal. Any feedback from former CDK users or developers will be welcome, though.</p>
<h2 style="text-align:justify;">GODI, Apt, Yum</h2>
<p style="text-align:justify;"><a href="http://godi.camlcity.org/godi/index.html">GODI</a>, <a href="http://en.wikipedia.org/wiki/Advanced_Packaging_Tool">Apt</a> and <a href="http://docs.fedoraproject.org/yum/en/">Yum</a> are the three main package managers available for OCaml users. The first one is OCaml-specific, the second one lives in Debian/Ubuntu/Knoppix world and the third one in the Red Hat/Fedora world.</p>
<p style="text-align:justify;">OCaml Batteries Included has no direct relation with either package manager, nor does it replicate the work of any of these managers. However, as Batteries relies on numerous external libraries, use of a package manager is strongly recommended.</p>
<h2 style="text-align:justify;">Findlib</h2>
<p style="text-align:justify;"><a href="http://www.camlcity.org/archive/programming/findlib.html">Findlib </a>is a compile-time package manager for OCaml. It lets developers easily specify which libraries are required and manages compile-time dependencies between installed libraries.</p>
<p style="text-align:justify;">OCaml Batteries Included uses Findlib extensively.</p>
<h1 style="text-align:justify;">Comparable projects, somewhere else</h1>
<h2 style="text-align:justify;">Python Batteries Included</h2>
<p style="text-align:justify;">To the best of our knowledge, the term &#8220;Batteries Included&#8221; was first coined by the Python community to describe the standard distribution of Python, which in addition to usual data structures, contains modules for handling databases, compression, a number of file format (de)coders including JSON, (X)HTML, XML, CSV, cryptography, logging, text interfaces, threading, inter-process communication, implementation of client and server protocols from SMTP to HTTP, communication with external webbrowsers, image and sound manipulation, internationalization, lexing and parsing, graphical interfaces, unit testing, sandboxing, reflexivity, OS-specific services, &#8230;</p>
<p style="text-align:justify;">This massive number of out-of-the-box features, along with the conciseness of the language, are probably the two main reasons of the success of Python: simple tasks, even those which require complex libraries, may often be programmed with only a few lines and in a few minutes.</p>
<p style="text-align:justify;">Well, obviously, we&#8217;re trying to provide something as useful with OCaml Batteries Included, but with the added safety and speed of OCaml.</p>
<h2 style="text-align:justify;">Haskell Batteries Included</h2>
<p style="text-align:justify;">The <a href="http://www.haskell.org/haskellwiki/Haskell_Platform">Haskell Platform</a> (although known as &#8220;Haskell Batteries Included&#8221;) is a recent project undertaken by the Haskell community with objectives comparable to OCaml Batteries Included: &#8220;provides a comprehensive,<br />
stable and quality tested base for Haskell projects to work from.&#8221;  At the time of this writing, the Haskell Platform has not released any software, although a first release is expected within a few weeks.</p>
<p style="text-align:justify;">As OCaml Batteries, the Haskell Platform is community-led and relies on a number of decentralized libraries.  As OCaml Batteries, the Haskell Platform requires package management.There are a number of differences in the methods, though.</p>
<p style="text-align:justify;">Some differences are probably trivial: where we strive to work with any of the major package management systems, the Haskell Platform is based solely on Cabal, which may allow them a better integration. Where OCaml Batteries attempts to reclassify existing libraries into one uniform hierarchy of modules, the Haskell Platform keeps the original module names, as decided by their original authors, pre-Platform.</p>
<p style="text-align:justify;">Others are quite far-reaching: OCaml Batteries provides an extended core of libraries to serve as support for standardization and uniformization, while the Haskell Platform doesn&#8217;t. Similarly, the Haskell Platform doesn&#8217;t add any uniformization layer, which means that the task of getting libraries to work together lies upon the end-user. Some Haskell libraries may be patched into compliance and uniformization, but this is not always possible, short of creating cyclic dependencies between libraries which should work together but don&#8217;t. Finally, the Haskell Platform has much stricter guidelines regarding libraries which can or can&#8217;t make it into the Platform. This is a good thing if library authors are willing to fix whichever problems prevent the inclusion of their work &#8212; something which we don&#8217;t assume for OCaml Batteries, at least not yet.</p>
<p style="text-align:justify;">Despite these differences, both projects seem based on solid foundations. And hopefully, both will achieve large success.</p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Batteries Included: Lazy Lists version 0.3]]></title>
<link>http://dutherenverseauborddelatable.wordpress.com/2008/05/18/batteries-included-lazy-lists-version-03/</link>
<pubDate>Sun, 18 May 2008 18:40:52 +0000</pubDate>
<dc:creator>yoric</dc:creator>
<guid>http://dutherenverseauborddelatable.wordpress.com/2008/05/18/batteries-included-lazy-lists-version-03/</guid>
<description><![CDATA[An updated version of the Lazy List module for OCaml has just been uploaded to Batteries Included an]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p style="text-align:justify;">An updated version of the Lazy List module for OCaml has just been uploaded to <a href="https://forge.ocamlcore.org/frs/?group_id=17">Batteries Included</a> and submitted to <a href="http://www.google.fr/url?sa=t&#38;ct=res&#38;cd=1&#38;url=http%3A%2F%2Fcode.google.com%2Fp%2Focaml-extlib%2F&#38;ei=v3cwSNH2MqPw-AL39cWIAg&#38;usg=AFQjCNEjWtWFbDXOLrRn-qxFA8-MlL8Ikg&#38;sig2=JV6e1hqE0-KmC5HpgwIeWQ">ExtLib</a>. See the release notes for more details.</p>
<p style="text-align:justify;">In addition, I am currently using this module to write a parser combinator library for OCaml. This library has reached early testing stage and will hopefully be added to Batteries Included soon.</p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Quelques mots de programmation paresseuse]]></title>
<link>http://dutherenverseauborddelatable.wordpress.com/2008/05/11/quelques-mots-de-programmation-paresseuse/</link>
<pubDate>Sun, 11 May 2008 20:20:22 +0000</pubDate>
<dc:creator>yoric</dc:creator>
<guid>http://dutherenverseauborddelatable.wordpress.com/2008/05/11/quelques-mots-de-programmation-paresseuse/</guid>
<description><![CDATA[Ces jours-ci, je travaille beaucoup avec et sur OCaml, que ce soit pour le projet ExtraPol (dont je ]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p style="text-align:justify;">Ces jours-ci, je travaille beaucoup avec et sur OCaml, que ce soit pour le projet ExtraPol (dont je finirai bien par vous glisser quelques mots) ou pour <a href="https://forge.ocamlcore.org/mail/admin/index.php?group_id=17">Batteries Included</a> (la rénovation en cours de la bibliothèque standard de OCaml). En particulier, je viens de finaliser un module de <a href="https://forge.ocamlcore.org/frs/shownotes.php?release_id=12">gestion des listes paresseuses</a>. Paresseuses ? Oui, paresseuses.</p>
<p style="text-align:justify;">Attardons-nous un moment sur le concept de <em>paresse</em> en programmation.</p>
<p><!--more--></p>
<h2>En Java (ou C++ ou C# ou &#8230;)</h2>
<p style="text-align:justify;">Presque tous les langages de programmation permettent, de manière restreinte, de définir des expressions paresseuses, c&#8217;est-à-dire des expressions dont le résultat n&#8217;est calculé que s&#8217;il peut influencer l&#8217;exécution du programme. Ainsi, en Java, nous pourrons définir la méthode <code>isEquals</code> suivante :</p>
<pre class="brush: java;">
bool isEquals(Object x)
{
  return x &amp;&amp; x.equals(myObject);
}
</pre>
<p style="text-align:justify;">Ici, au cours de l&#8217;exécution de cette fonction, <code>x.equals(myObject)</code> ne sera ffectivement évalué que si <code>x</code> n&#8217;est pas <code>null</code>. Java définit ainsi trois opérateurs paresseux <code>&#38;&#38;</code>, <code>&#124;&#124;</code> et <code>?:</code> : dans chacun des trois cas, à l&#8217;exécution du programme, la première opérande est évaluée avant de décider que faire de la deuxième (et de la troisième, dans le cas de <code>?:</code>). Ces constructions sont très utiles, mais sont loin de couvrir toutes les circonstances dans lesquelles la paresse peut s&#8217;avérer utile.</p>
<p style="text-align:justify;">Ainsi, imaginons un instant que nous souhaitons écrire une bibliothèque de journalisation, dont le rôle est d&#8217;enregistrer des messages dans un fichier au fur et à mesure de l&#8217;exécution du programme. Ajoutons une contrainte : les messages ne doivent être effectivement enregistrés que lorsque le journal est activé, par exemple parce que l&#8217;utilisateur est en mode de débogage :</p>
<pre class="brush: java;">
void log(String s)
{
   if(LOG) stream.println(s);
}
</pre>
<p>La méthode <code>log</code> sera typiquement invoquée des centaines de fois durant l&#8217;exécution du code, sous la forme suivante :</p>
<pre class="brush: java;">
void myFunction(int i, int j, int k)
{
   myLog.log(&quot;Entering myFunction(&quot;+i+&quot;, &quot;+j+&quot;, &quot;+j+&quot;)&quot;);
   //...
   myLog.log(&quot;Leaving myFunction()&quot;);
}
</pre>
<p style="text-align:justify;">Des centaines de fois, nous aurons donc besoin de calculer le résultat de <code>"Entering myFunction("+i+", "+j+", "+j+")"</code>, soit trois conversions d&#8217;entiers en chaînes de caractères, 6 concaténations de chaînes de caractères et un gaspillage de mémoire vive que nous pourrions éviter lorsque <code>LOG</code> est <code>false</code>. Comment faire pour éviter ce gaspillage ? La première possibilité consiste à exposer la valeur de <code>LOG</code> à tout le programme et à laisser le programmeur écrire plutôt</p>
<pre class="brush: cpp;">
void myFunction(int i, int j, int k)
{
   if(LOG) myLog.log(&quot;Entering myFunction(&quot;+i+&quot;, &quot;+j+&quot;, &quot;+j+&quot;)&quot;);
   //...
   if(LOG) myLog.log(&quot;Leaving myFunction()&quot;);
}
</pre>
<p style="text-align:justify;">Malheureusement, cette transformation nous oblige à contaminer tout le programme avec ce champ <code>LOG</code>, au détriment de la lisibilité et de l&#8217;extensibilité. En C ou C++, on pourrait cacher ce <code>LOG</code> derrière une macro, cette fois au détriment du débogage, de la modularité et de la robustesse. En Java, si nous n&#8217;avons pas une telle possibilité, nous pouvons utiliser des classes anonymes  pour arriver aux mêmes fins :</p>
<p>Commençons par modifier notre bibliothèque de journalisation :</p>
<pre class="brush: java;">
public interface LazyString
{
   public String toString();
}

void log(Object s)
{
   if(LOG) stream.println(s.toString());
}
</pre>
<p style="text-align:justify;">L&#8217;interface LazyString n&#8217;est guère contraignante : elle se contente de promettre que les objets qui l&#8217;implantent dispoent bien d&#8217;une méthode publique String toString(), qui servira à extraire de l&#8217;objet une chaîne de caractères. La différence avec ce qui précède est que nous pouvons nous débrouiller pour que la construction de la chaîne de caractères n&#8217;ait lieu qu&#8217;au moment où la méthode println est invoquée. En particulier, cette construction n&#8217;aura jamais lieu si <code>LOG</code> est false. Pour utiliser cette nouvelle version du journal, nous pourrons réécrire myFunction de la manière suivante :</p>
<pre class="brush: java;">
void myFunction(int i, int j, int k)
{
   myLog.log(new LazyString() { public String toString() { return &quot;Entering myFunction(&quot;+i+&quot;, &quot;+j+&quot;, &quot;+j+&quot;)&quot; });
   //...
   myLog.log(&quot;Leaving myFunction()&quot;);
}
</pre>
<p style="text-align:justify;">Est-ce la fin de l&#8217;histoire ? Pas tout à fait. Pour le moment, en Java, nous ne pouvons pas améliorer la syntaxe lors de l&#8217;appel de <code>log</code> &#8212; en tant que langage, Java est connu pour sa lourdeur syntaxique. En l&#8217;absence d&#8217;un préprocesseur digne de ce nom, nous ne pourrons rien faire de ce côté-là. Par contre, nous pouvons nous attaquer à un autre problème, qui se posera dès que nous serons confrontés à une méthode <code>log</code> à peine peu plus complexe :</p>
<pre class="brush: java;">
void log(Object s)
{
   if(LOG_TO_FILE) file_stream.println(s.toString());
   if(LOG_TO_WINDOW) text_pane.out.println(s.toString());
   if(LOG_TO_STATUSBAR) statusbar.setText(s.toString());
}
</pre>
<p style="text-align:justify;">Ici, <code>s.toString()</code> sera invoqué trois fois et calculera trois fois le même résultat. Pour un exemple aussi simple que le nôtre, ce n&#8217;est pas encore bien grave, mais on peut imaginer des contextes dans lesquels le calcul serait suffisamment long pour que ce délai soit inacceptable.</p>
<p style="text-align:justify;">Qu&#8217;à cela ne tienne, il suffit de réécrire <code>log</code> de manière à retenir la valeur de <code>s.toString()</code>. Voire mieux, nous pouvons encapsuler tout ceci dans une classe plus générique :</p>
<pre class="brush: java;">
public interface Result&lt;T&gt;
{
   public T getValue();
}
public abstract class Lazy&lt;T&gt;
{
   private boolean computed = false;
   private T value = null;
   /**
    * Proceeds to the actual computation.
    * This method will only be called once.
    */
   protected abstract T value();
   public final synchronized T getValue()
   {
      if(computed == false)
      {
        value       = value();
        computed = true;
      }
      return value;
   }
}

public abstract class LazyString extends Lazy&lt;String&gt;
{
   public String toString()
   {
      return this.getValue();
   }
}
</pre>
<p>En modifiant <code>myFunction</code> pour utiliser cette nouvelle version de <code>LazyString</code>, nous obtenons :</p>
<pre class="brush: java;">
void myFunction(int i, int j, int k)
{
   myLog.log(new LazyString() { protected String value() { return &quot;Entering myFunction(&quot;+i+&quot;, &quot;+j+&quot;, &quot;+j+&quot;)&quot; });
   //...
   myLog.log(&quot;Leaving myFunction()&quot;);
}
</pre>
<p style="text-align:justify;">Qu&#8217;avons-nous gagné ? Nous avons maintenant sous la main une interface Result&#60;T&#62; qui permet de représenter une opération. Si l&#8217;objet qui implante Result&#60;T&#62; est en fait un Lazy&#60;T&#62;, le calcul ne sera effectué que si son résultat est effectivement consulté. Si le résultat est consulté plusieurs fois (potentiellement par plusieurs threads différents), le calcul n&#8217;est toujours effectué qu&#8217;une seul fois.</p>
<p style="text-align:justify;">Nous venons d&#8217;ajouter une dose d&#8217;évaluation paresseuse à Java.</p>
<p style="text-align:justify;">Pour aller plus loin, regardons du côté de la programmation fonctionnelle &#8212; et des structures de données paresseuses.</p>
<h2>Du côté de la programmation fonctionnelle</h2>
<p>Commençons par réécrire en OCaml la première version de nos fonctions :</p>
<pre class="brush: python;">
let log s = if should_log then output stream s
</pre>
<p style="text-align:justify;">Nous venons de définir, comme en Java, une fonction nommée log, qui accepte comme paramètre un argument s &#8212; ici, une chaîne de caractères. Le contenu de la fonction log instruit d&#8217;écrire s sur un certain flux stream, si une certaine valeur booléenne should_log est vraie. Comme en Java, nous invoquerons cette fonction depuis my_function, à l&#8217;aide de :</p>
<pre class="brush: python;">
let my_function i j k =
  log (sprintf &quot;Entering my_function %i %i %i&quot; i j k);
  (* ... *)
  log &quot;Leaving my_function&quot;
</pre>
<p style="text-align:justify;">Jusque-là, à part les parenthèses et l&#8217;utilisation de la fonction <code>sprintf</code>, rien de bien différent. De nouveau, il est nécessaire de convertir i, j et k en chaînes de caractères et de procéder à quelques concaténations.</p>
<p style="text-align:justify;">À partir d&#8217;ici, comme en Java, nous pouvons définir équivalent de Lazy&#60;T&#62;, que nous appellerions probablement <code>'a lazy</code>, doté d&#8217;un constructeur qui acceptera en argument une fonction dont le résultat est un <code>'a</code>, etc. La transformation de notre code pour convertir log en appel paresseux est globalement similaire à ce que nous ferions en Java. De fait, tout ceci n&#8217;est pas nécessaire puisque puisque la programmation paresseuse est en fait une primitive du langage. Le mot-clé <code>lazy</code> introduit une expression de type <code>'a Lazy.t</code>, c&#8217;est-à-dire dont le résultat est de type <code>'a</code> et ne sera calculé que s&#8217;il est nécessaire. La fonction <code>force : 'a Lazy.t -&#62; 'a</code> permet alors de consulter le résultat d&#8217;une valeur ainsi mise en réserve, en la calculant au passage si nécessaire. De fait, notre version définitive ressemblera à</p>
<pre class="brush: python;">
open Lazy
let log s =
  if should_log_1 then output stream_1 (force s);
  if should_log_2 then output stream_2 (force s);
  if should_log_3 then output stream_3 (force s)

let my_function i j k =
  log (lazy (sprintf &quot;Entering my_function %i %i %i&quot; i j k));
  (* ... *)
  log (lazy &quot;Leaving my_function&quot;)
</pre>
<p style="text-align:justify;">Notons au passage qu&#8217;il ne serait pas possible de réécrire directement lazy sous la forme d&#8217;une fonction OCaml. Pour réinventer <code>lazy</code>, il serait nécessaire de passer par Camlp4 ou d&#8217;accepter des notations légèrement plus lourdes.</p>
<h2>Et alors ?</h2>
<p style="text-align:justify;">Il va falloir que je vous avoue quelque chose : je me moque complètement de la fonction <code>log</code>. Dans la programmation paresseuse, le plus intéressant, c&#8217;est bien les structures de données.</p>
<p>Tenez, en Java, nous pouvons très bien redéfinir la notion d&#8217;itérateurs de la manière suivante :</p>
<pre class="brush: java;">
public interface MyIterator&lt;T&gt;
{
  public Lazy&lt;MyIterator&lt;T&gt;&gt; next();
  public T value;
}
</pre>
<p>Un itérateur est ici un objet capable de contenir une valeur&#8230; et de renvoyer un autre itérateur, qui représente la suite de l&#8217;itération. On écrira de même en OCaml :</p>
<pre class="brush: python;">
type 'a my_iterator = Iterator of 'a * 'a my_iterator Lazy.t
</pre>
<p>Complétons cette définition pour nous autoriser à construire des itérations <em>finies</em> :</p>
<pre class="brush: python;">
type 'a node =
 &amp;#124; Nil
 &amp;#124; Cons of 'a * 'a lazy_list
type 'a lazy_list = ('a node) Lazy.t
</pre>
<p style="text-align:justify;">Nous venons de définir la notion de <em>liste paresseuse</em>. Une liste paresseuse est une valeur paresseuse qui, une fois évaluée, produit soit le résultat <code>Nil</code> (la liste vide), soit un résultat de la forme <code>Cons(h, t)</code> où <code>h</code> est une valeur et <code>t</code> est la suite de la liste paresseuse.</p>
<p style="text-align:justify;">À partir de cette définition, nous pouvons construire une fonction <code>range</code>, qui servira à représenter un intervalle d&#8217;entiers :</p>
<pre class="brush: python;">
let rec range i j =
 if i &gt;= j then Nil
 else            range (i+1) j
</pre>
<p>Ainsi, en OCaml, <code>range 0 100000</code> calculera les nombres 0, 1, &#8230;, 100000 au fur et à mesure qu&#8217;ils seront demandés.</p>
<p style="text-align:justify;">À partir de là, nous pouvons définir les transformations habituelles sur les structures de données. Ainsi, pour convertir (paresseusement) une liste paresseuse en une autre, comme pour les listes habituelles, nous pouvons définir la fonction <code>map</code> :</p>
<pre class="brush: python;">
let rec map f l = match force l with
  &amp;#124; Nil            -&gt; lazy Nil
  &amp;#124; Cons (h,t) -&gt; lazy Cons (f h, map f t)
</pre>
<p style="text-align:justify;">Cette fonction <code>map</code>, qui sera l&#8217;un des premiers outils que nous placerons dans notre bibliothèque de gestion des listes paresseuses, applique une fonction successivement à chaque élément d&#8217;une liste paresseuse, de manière à obtenir une nouvelle liste paresseuse. Pour doubler tous les éléments de notre intervalle précédent, nous écrirons donc</p>
<pre class="brush: python;">
map (( * ) 2) (range 0 100000)
</pre>
<p style="text-align:justify;">De la même manière, nous pouvons définir le nécessaire pour filtrer les éléments d&#8217;une liste afin de n&#8217;en garder que ceux qui vérifient une propriété donnée, le tout sous la forme d&#8217;une fonction <code>filter</code>, qui rejoindra <code>map</code> dans notre bibliothèque standard.</p>
<pre class="brush: python;">
let rec filter f l = match Lazy.force l with
  &amp;#124; Nil -&gt; lazy Nil
  &amp;#124; Cons(h,t) -&gt; lazy (Cons (f h, filter t))
</pre>
<p style="text-align:justify;">Et à ce prix-là, définissons une fonction <code>iter</code>, tout aussi standard, qui nous permettra d&#8217;appliquer une fonction impérative à tous les éléments d&#8217;une liste paresseuse, de la même manière que la boucle for de Python ou la nouvelle boucle for de Java :</p>
<pre class="brush: python;">
let rec iter f l = match force l with
  &amp;#124; Nil            -&gt; ()
  &amp;#124; Cons (h,t) -&gt; f h; iter t
</pre>
<p style="text-align:justify;">En combinant tout ce qui précède, nous pouvons faire afficher les carrés parfaits de l&#8217;ensemble des nombres pairs des nombres de l&#8217;intervalle [0; 100000].</p>
<pre class="brush: python;">
let is_square x =
  let y = int_of_float (sqrt (float_of_int x)) in
  x = y * y

iter print_int (filter is_square (map (( * ) 2) (range 0 100000)))
</pre>
<p style="text-align:justify;">Comme la liste n&#8217;est consultée qu&#8217;une seule fois, grâce à la magie de la paresse et du ramasse-miettes, tout ceci se fera fait sans avoir à allouer la mémoire vive nécessaire pour retenir tous les entiers entre 0 et 100000.</p>
<p style="text-align:justify;">Jusqu&#8217;ici, toutes les opérations que nous avons vues auraient pu être implantées, peut-être avec quelques difficultés, en utilisant des <em>itérateurs</em> ou des <em>générateurs</em>, tels que ceux que définissent les bibliothèques Java, C#, JavaScript, Python, OCaml etc. Tout comme les listes paresseuses, les itérateurs et générateurs permettent de représenter des suites d&#8217;informations qui ne seront effectivement calculées que lorsqu&#8217;elles seront effectivement consultées. Mais à la différence des listes paresseuses, les itérateurs/générateurs oublient les informations immédiatement après les avoir fournies. Si les itérateurs et générateurs sont très utiles &#8212; et probablement plus optimisés que les listes paresseuses pour les exemples précédents &#8212; leur champ d&#8217;application s&#8217;arrête dès que les données peuvent être utilisées plusieurs fois.</p>
<p style="text-align:justify;">Ainsi, considérons l&#8217;une des applications majeures des itérateurs/générateurs et des listes paresseuses : l&#8217;analyse de langages, et plus particulièrement l&#8217;analyse lexicale et syntaxique d&#8217;un code source. Le plus fréquemment, on commence par considérer le fichier d&#8217;entrée comme un itérateur/générateur de caractères. Un filtre, l&#8217;<em>analyseur lexical</em>, s&#8217;applique alors à cet itérateur pour le transformer en itérateur de <em>lexèmes</em> &#8212; c&#8217;est le rôle d&#8217;outils tels que Lex, Flex ou, aussi surprenant que cela puisse paraître, Sax. Ce deuxième itérateur est alors consommé par un <em>analyseur syntaxique</em>, dont le rôle est de produire un arbre de syntaxe abstraite, qui représentera le code source en mémoire vive &#8212; c&#8217;est, cette fois, le rôle d&#8217;outils tels que Bison, Yacc ou DOM. Ces derniers, ainsi que [presque tous] leurs concurrents, consultent à l&#8217;avance k lexèmes depuis l&#8217;itérateur de lexème, pour un certain k fixé lors de la conception de l&#8217;analyseur, émettent des hypothèses en partant de ces k lexèmes, et ne peuvent pas revenir en arrière, en cas d&#8217;erreur, de plus de k pas. À l&#8217;inverse, et de manière très schématique, les combinateurs de parseurs construits sur des listes paresseuses, tels que Parsec (pour Haskell) ou PLC (pour OCaml), ne sont pas limités par ces k lexèmes d&#8217;avance, puisqu&#8217;ils peuvent à volonté revenir en arrière dans la liste. Un élément ne disparaît de la liste paresseuse que lorsque sa valeur ne peut plus influencer l&#8217;exécution du programme. Cette conception à base de listes paresseuses se paye en termes de performances et de consommation de mémoire mais offre beaucoup plus de flexibilité, notamment la possibilité de construire ou de modifier dynamiquement des analyseurs syntaxiques.</p>
<p style="text-align:justify;">Avant de conclure sur la comparaison entre itérateurs et listes paresseuses, précisons brièvement qu&#8217;il est aisé de construire l&#8217;un par-dessus l&#8217;autre : un itérateur est une liste paresseuse qui oublie ses éléments au fur et à mesure qu&#8217;ils sont consommés, alors qu&#8217;une liste paresseuse est un itérateur qui retient ses éléments jusqu&#8217;à intervention du ramasse-miettes. Ainsi, dans la bibliothèque sur laquelle je travaille, la conversion d&#8217;une liste paresseuse en itération s&#8217;est écrite, jusqu&#8217;à une récente optimisation :</p>
<pre class="brush: python;">
let enum l =
  let reference = ref l in
    Enum.from (fun () -&gt; match next !reference with
			 &amp;#124; Cons(x,t) -&gt; reference := t; x
			 &amp;#124; Nil          -&gt; raise Enum.No_more_elements )
</pre>
<p style="text-align:justify;">Dans cet extrait de code, nous commençons par déclarer une variable modifiable nommée <code>reference</code> et qui désignera initialement le premier élément de la liste. Nous définissons alors une fonction anonyme qui, lorsqu&#8217;elle sera appelée, renverra l&#8217;élément actuel désigné par <code>reference</code>, ou, si nous avons dépassé la fin de la liste, lancera une exception <code>Enum.No_more_elements</code> pour signaler que l&#8217;itérateur est terminé. La fonction <code>Enum.from</code> construit l&#8217;itérateur dont le comportement est donné par cette fonction.</p>
<p style="text-align:justify;">À l&#8217;inverse, pour convertir un itérateur en liste paresseuse, nous employons la fonction suivante :</p>
<pre class="brush: python;">
let of_enum e =
  let rec aux () =
    lazy (match Enum.get e with
      &amp;#124;	Some x -&gt; Cons (x, aux () )
      &amp;#124; None    -&gt; Nil )
  in aux ()
</pre>
<p style="text-align:justify;">Dans l&#8217;extrait, nous commençons par définir une boucle que nous nommons <code>aux</code> et dons le contenu est paresseux. À chaque fois qu&#8217;un nouvel élément de la liste est consulté, nous consommons la valeur du prochain élément de notre itérateur. Si celui-ci existe, nous l&#8217;appelons <code>x</code>, et ce <code>x</code> constitue le prochain élément de notre liste, tandis que la suite de la liste sera donnée par le résultat de la prochaine invocation de la boucle <code>aux</code>. Dans le cas contraire, la liste est terminée.</p>
<h2>Références :</h2>
<ul>
<li>Pour plus de détails sur la distinction entre générateurs, itérateurs et listes paresseuses, je vous invite à consulter <a href="http://home.pipeline.com/~hbaker1/Iterator.html">Iterators: Signs of Weakness in Object-Oriented Languages, par Henry G. Baker</a>.</li>
<li>Pour plus de détails sur le module de listes paresseuses de Batteries Included, le code source est disponible <a href="https://forge.ocamlcore.org/frs/shownotes.php?release_id=12">sur OCaml Forge</a>. Le code source du module Enum, qui définit des itérateurs pour OCaml, est disponible avec le reste de la bibliothèque ExtLib, <a href="http://code.google.com/p/ocaml-extlib/">sur Google Code</a>.</li>
<li>Pour plus de détails sur la programmation paresseuse en OCaml, le <a href="http://caml.inria.fr/pub/docs/manual-ocaml/libref/Lazy.html">guide de référence</a> est à votre disposition.</li>
<li>Enfin, rendons à César ce qui appartient à César. Si l&#8217;évaluation paresseuse vous intéresse, vous êtes vivement encouragés à vous informer sur le langage de programmation Haskell, dans lequel toutes les évaluations sont paresseuses par défaut.</li>
</ul>
</div>]]></content:encoded>
</item>

</channel>
</rss>
