https://wiki.haskell.org/api.php?action=feedcontributions&user=HenkJanVanTuyl&feedformat=atomHaskellWiki - User contributions [en]2024-03-19T01:41:19ZUser contributionsMediaWiki 1.35.5https://wiki.haskell.org/index.php?title=User:HenkJanVanTuyl&diff=9842User:HenkJanVanTuyl2006-12-31T10:54:43Z<p>HenkJanVanTuyl: </p>
<hr />
<div>I changed my user id to [http://haskell.org/haskellwiki/?title=User:Henk-Jan_van_Tuyl Henk-Jan van Tuyl].<br />
<br />
[[User:HenkJanVanTuyl|HenkJanVanTuyl]] 10:45, 31 December 2006 (UTC)</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=User:HenkJanVanTuyl&diff=9841User:HenkJanVanTuyl2006-12-31T10:46:46Z<p>HenkJanVanTuyl: </p>
<hr />
<div>I changed my user id to "Henk-Jan van Tuyl".<br />
<br />
[[User:HenkJanVanTuyl|HenkJanVanTuyl]] 10:45, 31 December 2006 (UTC)</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=User:HenkJanVanTuyl&diff=9840User:HenkJanVanTuyl2006-12-31T10:45:31Z<p>HenkJanVanTuyl: </p>
<hr />
<div>I change my user id to Henk-Jan van Tuyl.<br />
<br />
[[User:HenkJanVanTuyl|HenkJanVanTuyl]] 10:45, 31 December 2006 (UTC)</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=HIDE&diff=9839HIDE2006-12-31T10:41:02Z<p>HenkJanVanTuyl: </p>
<hr />
<div>[[Category:Orphaned projects]][[Category:Applications]] [[Category:Proposals]] [[Category:Development tools]]<br />
<br />
'''This project is currently orphaned.''' Creating a decent IDE is still an active research topic and many problems are yet to be resolved (see, say, the Proxima thesis). It has to be noted, however, that no conceptual problem was encountered with the GHC API. The plugin system -- which was based on GHC API -- remained at the prototype stage though.<br />
<br />
----<br />
<br />
I'd like to start a discussion on the development of an IDE for Haskell which for the moment we will call hIDE 2.x.<br />
<br />
There is another wiki page (HaskellIde) on Haskell IDEs in general, however here I'd like to focus the discussion on more specific proposals for the development of an IDE.<br />
<br />
There is also a hIDE page at [http://haskell.org/hide/ haskell.org] where developers may blog, comment on blogs, executables will be available for Windows users, tarballs for geeks, etc...<br />
<br />
If you're looking for specific low-level details of hIDE itself, try the [[HIDE/Design]] page.<br />
<br />
So let me (DuncanCoutts) start with my own personal views.<br />
<br />
== A Manifesto for hIDE 2.x ==<br />
<br />
* Free Software, not based off proprietary IDEs<br />
* Written in Haskell<br />
* Extensable plugin-based design using hs-plugins (like lambdabot)<br />
* Distributed development using darcs (again like lambdabot)<br />
* Attractive UI using Gtk+ 2.x and the Glade visual GUI builder)<br />
* Internal editor also written and extensible in Haskell, based on Yi<br />
* Build system based on Cabal<br />
* Integration with other tools, haddock, analysis and refactoring etc. through plugins<br />
* Use the new GHC internal interface to provide tight integration with the editor and other parts of the IDE<br />
<br />
BulatZiganshin: how about targeting competition with Emacs, VIM and other wide-spread IDEs/editors? i think now it's the right time to roll up Haskell-based killer app :)<br />
DonStewart: I think we are doing this, yes, since Yi already attempts to provide emacs/vim/... interfaces<br />
<br />
=== Development style ===<br />
<br />
Now you may notice that I'm mixing features with implementation choices (eg using Gtk+). To justify this I would appeal to the open source idea to release early and the importance of getting a base platform working to gain momentum and mindshare. Therefore we should not necessarily try to generalise too much too early but to get the basics working quickly.<br />
<br />
However the other thing to avoid is going all out for features and neglecting the structure and organisation of the code necessary to allow sustained growth. This is one of the main lessons we must take from hIDE 1.x. So the modular development by plugins is important to allow distributed and parallel development. So for example it needs to be possible to develop components and then swap them out later for improved versions. For example suppose we start by using an editor component based on the GtkSourceView widget, it should be possible to develop another editor component based on Yi while the default remains as GtkSourceView and to have them both plugins loaded at the same time. Then when the new component is deemed to be better then the default implementatin can be changed over. That way we do not need to hold up immediate practicalty by waiting for more advanced implementations.<br />
<br />
=== Why now? ===<br />
<br />
Because we have the technology and there is demand for tools to make writing and maintining Haskell code easier.<br />
<br />
* We now have distributed development with darcs.<br />
* We now have hs-plugins and the techniques for building fully dynamic modular applications. http://www.cse.unsw.edu.au/~dons/papers/SC05.html<br />
* We now have the GHC interface so we can get really good integration with the compiler.<br />
* We now have mature GUI libraries.<br />
* We now have an editor written in Haskell being developed.<br />
* There is a VisualStudio plugin being written and we don't want Free software for Haskell development to be left behind.<br />
* The [http://haste.dyndns.org:8080/ Haste] developers demonstrated that it's possible to build an IDE shell in a couple months with inexperienced Haskell developers.<br />
<br />
=== Why in Haskell? ===<br />
<br />
* Because Haskell developers know Haskell and not elisp/Java/C++/COM/C#. Therefore we are only likely to be able to get Haskell developers to help out by writing extensions if they can do so in Haskell.<br />
* It means we do not have to live with the requriements and limitations of IDEs that are primarily aimed at other languages or written in other languages.<br />
* Let me repeat a point: The [http://haste.dyndns.org:8080/ Haste] developers demonstrated that it's possible to build an IDE shell in a couple months with inexperienced Haskell developers.<br />
<br />
== Potential contributors ==<br />
<br />
Since this is supposed to be a distributed development thing I thought it'd be nice if we start a list of people who have expressed any interest in contributing in any fashion.<br />
<br />
So the list of potential contributers. Use you name and/or irc nick. If you have any specific ideas for what you might be interested in constibution then feel free to mention them. Or perhaps any skills you think would be useful.<br />
<br />
* DuncanCoutts (dcoutts): I work on Gtk2Hs so I know a bit about Gtk+ programing in Haskell. I think I know a little abut GUI design (but maybe I'm just opinionated and igonrant). I used to work on hIDE 1.x. I would be interested in looking at GUI issues and general design and management issues.<br />
* DavidHimmelstrup (Lemmih): I've written parts of hs-plugins and I'm fairly familiar with GHC's internal interface.<br />
* NeilMitchell (ndm): I like IDE's. I also like Windows so will keep it compiling on that.<br />
* DonStewart (dons): I hacked up hs-plugins and Yi, developed the dynamic architecture stuff, and am interested in pushing Haskell as a dynamic extension language.<br />
* SamGoldman (Speck): I'm a relative newbie with Haskell, but I could test for OS X. I am also good with web apps and aesthetics/ergonomics stuff.<br />
* Gour (gour): I'm also Haskell newbie, but can help test on amd64, web stuff...more after acquiring some Haskell-related skills.<br />
* LyndonTremblay (humasect): Into realtime use for completely synthetic and dynamic audio and graphics processing, sometimes synaesthetically, so anywhere between general advisory to overt hack contributioning related to concurrent development, in place editing slash modifying of code and/or data link up slash external tool integration.<br />
* DominicFox (poetix): Would like to build and test periodically on Linux, maybe assist with documentation.<br />
* DavidWaern (davve): Was a member of the Haste project. Interested in the general design and most coding parts of the project. You can read about my [http://haste.dyndns.org:8080/member.php?id=4 experiences] when developing Haste.<br />
* LennartKolmodin (kolmodin): Member of the [http://haste.dyndns.org:8080/ Haste] project ([http://haste.dyndns.org:8080/member.php?id=5 developer log]) when it was active. Would like to look into general design and "code aware" features along with overall hacking.<br />
* JeanPhilippeBernardy: I hacked the emacs keybindings of Yi. I'm looking forward to perfect those.<br />
* ImamTashdidulAlam: I have ideas :-)<br />
<br />
Useful contributions may be in many forms, not just coding:<br />
<br />
* Coding (well obviously we need some coding!)<br />
* Ideas requriements and design<br />
* GUI design - yes this is important<br />
* Web stuff<br />
* Graphics - eg logos maybe other stuff<br />
* Documentation<br />
* Cheerleading!<br />
<br />
== Development issues ==<br />
<br />
Feel free to extend any of these with your own opinions.<br />
<br />
There is now a [[hIDE/Design]] page for documenting the internal design.<br />
<br />
=== The Windows question ===<br />
<br />
ie "''will it run on Windows?''"<br />
<br />
I don't see any reason why it shouldn't. All the basic parts we are using are cross-platform. Gtk2hs and hs-plugins run on Unix, Windows and Mac OS X.<br />
<br />
That's not to say that every plugin will necessarily build on every platform. In fact we can use the plugin architecture to our advantage here to use different technologies to implement components on different platforms and to get better platform integration. For example consider the configuration subsystem; we might want to use GConf when using the Gnome platform but the Windows registery on Windows (or a simple file based implementaion on Windows and non-Gnome Unix platforms if people hate those sort of technologies).<br />
<br />
However I don't expect that the plugability would extend to the basic graphical toolkit that is used. Though I do expect that many plugins would not need to be directly linked to a GUI toolkit if they do not need complicated user interaction.<br />
<br />
BulatZiganshin: if using GTK will lead to need of installing additional software for Windows users of IDE, then it will be better to use wxWindows?<br />
<br />
Piotr Kalinowski: I don't expect IDE to be such sort of software that you would frown at a single additional install of GTK libs. Would you?<br />
<br />
Jeremy O'Donoghue: Agree that look and feel is not primary for IDE. However, wide acceptance will require that a Windows/Mac Installer be created, and this really needs to include all necessary libs to run.<br />
<br />
For wxHaskell this is easy (2 DLLs in same directory as application, totalling ~4MB, and no registry entries needed). I've created several applications this way using Inno Setup - it's very easy to do.<br />
<br />
For GTK it is harder - my Windows GTK directory contains 1850 files and 47MB (I assume similar problem for native Mac port - but I've never even tried: GTK was just too painful (and ugly) on Windows). You'll also need to create a few registry entries (HKLM/SOFTWARE/GTK/2.0/DllPath, Path and Version last time I looked).<br />
<br />
It took me a week of part-time hacking to get a GTK+ developer install which worked, and I've never succeeded in packaging it into a single installer with a Windows app which reliably works. While GUI lib used for an IDE doesn't necessarily matter, packaging is a very big deal for a 'killer app' - it really does need to have a simple installation mechanism, so someone should look into this right from the start.<br />
<br />
For me personally, I'd much prefer native look and feel on all platforms, but I'd live without it if HIDE was really a 'killer app'. However, failing to create a simple installation mechanism and/or hundreds of library dependencies mean that I'd probably never even try it to find out (life's too short).<br />
<br />
Piotr Kalinowski: I suppose it is possible to use a separate installer for GTK+. How about the one from http://gladewin32.sourceforge.net ? There's runtime environment and development version. Personally I never had problems with GTK under windows at looks very well for me.<br />
<br />
=== The GHC question ===<br />
<br />
ie "how much GHC integration?"<br />
<br />
The concern here is about a potential tradeoff between advantages of integration well with GHC (eg using it's API to get lots of info) and the disadvantage that it might become too hard to use anything other than GHC for the build.<br />
<br />
It think that its clear that hIDE itself will require GHC to build because at the moment hs-plugins and Gtk2Hs do not support any other Haskell system. However if we can use different build systems or Cabal then it should be quite possible to target other Haskell systems like hugs,nhc,jhc etc. It may well be that we rely on GHC for some of the IDE features like error reporting and jump to definition etc.<br />
<br />
[[Yhc]] is in the process of defining a [[Yhc/API/Compiler|Yhc API]], if the list of features required was specified somewhere, perhaps with some abstract API that meets HIDE's needs, I'm sure Yhc could support it too (in time).<br />
<br />
BulatZiganshin: there must be standard interface to compilers with one implementation using GHC, another - HUGS and so on<br />
<br />
=== The license question ===<br />
<br />
ie "What license? GPL? LGPL? Other?"<br />
<br />
Personally I would normally pick GPL for applications, however this would not be a normal application.<br />
<br />
In this instance I would suggest LGPL for the core components since we want people to contribute extensions for their favourite tools. This may necessitate linking against existing code under other licenses. Authors may be reluctant to effectively relicense any code they include as GPL. IANAL but I believe LGPL would allow combining plugins with different licenses.<br />
<br />
However of course we can only accept plugins in the official collection/distribution that are Free Software (since otherwise of course we cannot legally distribute them).<br />
<br />
BulatZiganshin: are BSD license not appropriate?<br />
<br />
Jeremy O'Donoghue: I don't see how HIDE can be anything except LGPL or GPL, given GTK licensing (it is would be hard to argue that it is not a derived work of GTK). This is another (big) difference of wxHaskell where license is more liberal, and would allow open or closed source plug-ins etc (whether this is an advantage depends on point of view).<br />
<br />
Without getting into 'license' war, BSD or MIT license should be OK for a plug-in to LGPL/GPL licensed application. Release a closed source plug-in at your own risk (I wouldn't do it, for example, based on the legal advice I have received, but your legal advice may vary).<br />
<br />
Piotr Kalinowski: Actually it is just an application linked against GTK+ and not a GUI library based on it. Since GTK+ is licensed under LGPL, that should impose no restrictions on HIDE license. Personally, I don't care as long as it is open source.<br />
<br />
=== Plugins ===<br />
<br />
Record of a stuff from #haskell. TODO: Format this better as issues get worked out.<br />
<br />
<Lemmih> dcoutts: Do we need to handle editor plugins differently from non-editor plugins<br />
or can we device some universal plugin API which handles both? Can plugins depend<br />
on each other? Can two editor plugins be loaded at the same time?<br />
<dcoutts> Lemmih, I don't think there is single plugin API, there is a set of plugin<br />
interfaces. Some plugins will provide interfaces that other plugins can use. Yi<br />
would be one such example. The IDE shell would be another<br />
<dcoutts> plugins must be able to depend on each other<br />
<dcoutts> wether more than one plugin of the same variety can be loaded at once depends on<br />
the provider of the interface<br />
<dcoutts> eg how does an editor plugin hoist itself in? probably by registering itself with<br />
the IDE shell. So if the IDE shell allows more than one they yes.<br />
<dcoutts> I expect that for some kinds of plugins it'll make perfect sense to have multiple<br />
ones loaded at once, others it might be only one at once with a user preference<br />
to select which and others might be fixed (eg core plugins, or platform support<br />
plugins)<br />
<dcoutts> Lemmih, so the whole thing is a bunch of modules, there's no base app with a<br />
single unified plugin interface. The whole thing is plugin modules.<br />
<dcoutts> plugins can be linked against each other so they canprovide interfaces to each other<br />
<dcoutts> each plugin gets an initialisation function. That allows it to register with any<br />
interfaces it intends to implement<br />
<dcoutts> eg the core IDE shell plugin will provide interfaces for adding menu items and such<br />
like other plugins will linkagainst the IDE shell plugin and use it's public<br />
interface to register hooks like menu items<br />
<br />
BulatZiganshin: see FreeRIDE below, they already developed powerful and flexible plugin architecture<br />
<br />
=== UI ideas ===<br />
<br />
Most IDE user interfaces look pretty similar these days. That's no bad thing particularly. It certainly gives us a good place to start and plenty of suggestions to think about. Here's a breif list of other IDEs that it would be worth gathering ideas from:<br />
<br />
* Eclipse - a highly extendable IDE for Java and other language. There is a detailed and interesting [http://www.eclipse.org/articles/Article-UI-Guidelines/Contents.html UI design document] available.<br />
* [http://anjuta.sourceforge.net/screen-shots Anjuta] - a Gtk/Gnome IDE for C/C++<br />
* [http://www.kdevelop.org/index.html?filename=3.2/screenshots1.html KDevelop] - a KDE IDE for C/C++<br />
* [http://haste.dyndns.org:8080/screenshots.php Haste] - a Haskell IDE developed for a univerty student project.<br />
* MS VisualStudio - many people swear by it. Can't find many screenshots however.<br />
* [http://slickedit.com/ SlickEdit] - apparently is quite good<br />
* LEO [http://webpages.charter.net/edreamleo/front.html] - User definable outlines and views to code. Not mainstream but good for ideas. See below for a mockup.<br />
* [http://pida.berlios.de/index.php/Main_Page PIDA ] - Python Integrated Development Application - this one looks interesting sharing some of our hIDE ideas <br />
Feel free to add more to this list.<br />
* [http://freeride.rubyforge.org/wiki/wiki.pl FreeRIDE] - Ruby IDE, useful because of similar architecture and motivation, reasonably well designed plugin architecture.<br />
<br />
The Eclipse example is particularly interesting because it is also a highly plugable IDE. They can't fix upon a single static UI design because it mught be extensable by many plugins. So it has to pick UI concepts and abstractions that many plugins can fit into. At least the first part of the [http://www.eclipse.org/articles/Article-UI-Guidelines/Contents.html eclipse UI design document] is well worth a read.<br />
<br />
Let me (DuncanCoutts) now note down some of my UI ideas:<br />
<br />
First of all, here is a little UI mockup.<br />
<br />
http://haskell.org/~duncan/hIDE/hIDE%20main%20window.png<br />
<br />
Yep is looks much like many other IDEs.<br />
<br />
So we have 3 pannels and a menu and toolbar. The main focus is of course the editor pane. The other two can be hidden away to get the maximum screen space for the editor (which is a common IDe problem, that they take up too much screen space).<br />
<br />
The left hand pane is a collection of views of pages, or rather a way of navigating between pages. So they are lists or trees of pages. Selecting a page opens that page in the main editor pane. The reason for having several tabs of these views is that sometimes we need different views. For exaple, in the UI mockup above we've got a modules and files view. The files view would list all files in the project directory tree, selecting any of the would allow you to edit the file. The modules view on the other hand lists just the Haskell modules rather than all files. This is probably more useful when doing ordinary coding. The modules view would list all files that are the primary source, so for example when using a module that needs pre-processing, eg a .hsc .chs. .y or .x file, then the modules view would show only that file and not the generated .hs file - the generated file would be visible through the files view.<br />
<br />
The intention is that plugins could register their own views. What I am describing is an example of a possible default configuration. To make the vies/pages system quite flexable one idea is that all pages should exist in a singe rooted tree and that the tab are multiple views into that tree. For example the configuration in the mockup could be described as having four views, rooted at /files, /modules, /open, /tools respecvely. That would allow the user to configure it with one single tree view rather than having multiple tabs. It would also allow a view to show only a subtree, which might be useful perhaps in a very large code base. So in that scheme, plugins could register pages or whole subtrees of pages and setup views to look into them.<br />
<br />
The third pannel is for tools to display a UI to interact with the user. Examples of these tools could include things like search, interactive window, error messages etc. anything which you might want to have open at the same time as an editor page.<br />
<br />
-----<br />
<br />
Another vision for hIDE is<br />
http://www.cc.jyu.fi/~aleator/yide.png. (how do I get a link<br />
instead of picture?)<br />
<br />
On the left there is user editable treeview which can contain different types of nodes. Some nodes will become<br />
modules and others are there for documentation. <br />
Some nodes can contain interpreters that have other<br />
modules loaded or other dynamic output. Nodes can be<br />
cloned so same node can appear at multiple places in<br />
the tree.<br />
<br />
Point is that the whole tree is user built. (Though you can provide templates for lazy.)<br />
<br />
Right side is normal splittable editing panel.<br />
<br />
The point is that everything is in the outline. You (user) can make different nodes for different things and organize your workflow as you like. You could build different<br />
views, for say, bugs. Just collect all things related to a bug (tests, code, documentation) into single outline for easy manipulation and reference.<br />
<br />
Different types of nodes is the main extension direction of this plan (besides editor interface with type checking etc).<br />
You could even make nodes that contain extension code that <br />
dynamically produces the node contents so as to make extending<br />
easy.<br />
<br />
You could start with simple @module node that reflects a haskell module. Then Ordinary node that will be just documentation. Then perhaps ghci node, build log node, timetable node, project management node.. (see pivotal for<br />
great inspiration)<br />
<br />
<br />
== Development ==<br />
<br />
darcs get http://darcs.haskell.org/hIDE<br />
darcs get http://darcs.haskell.org/yi<br />
<br />
You'll also need [http://www.cse.unsw.edu.au/~dons/fps.html fps] and unless you are using >=GHC 6.5 also [http://scannedinavian.org/~lemmih/ghc-api ghc-api].<br />
<br />
So far it has the basics of plugin loading ability and we've passed the first milestone of getting it to boot, load plugins, intialsise them and transfer control to the UI. It now is also hosting the yi/hIDE integration work which is comming along nicely.<br />
<br />
== Name contest ==<br />
<br />
Please put your suggestion for the official name of hIDE here...<br />
<br />
* hs as Haskell Studio :-) (Gour)<br />
* yiDE - since it uses yi and is an IDE (aleator)<br />
* gYi - ditto :) (BulatZiganshin)<br />
* A name prefixed with "Free" or "Open", i.e. FreeIDE, FreeCode(?), FreeHS, FreeStudio, OpenStudio etc. (to emphasise that we are free!) (dons)<br />
* On the 'Studio' theme, the obvious one is "HaskellStudio". But the MS haters might object :-) (dcoutts)<br />
** well, the full name of [http://anjuta.sourceforge.net/ Anjuta] is "Anjuta DevStudio" :-) (gour)<br />
* why not hIDE? -- Could we make a clever pun with a companion tool named "Jekyll" or something like it? (mwc)<br />
* Hive: Haskell Integrated Visual Editor (Environment), or somesuch. "The hive is a symbol of industriousness and teamwork." http://www.google.be/search?q=define%3Ahive<br />
* Hades: '''HA'''skell '''DE'''velopment '''S'''ystem (or '''S'''tudio)<br />
** Since the greek word “hades” denotes the place where the dead reside, it seems not like a good name for a Haskell IDE to me. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 00:11, 21 February 2006 (UTC)<br />
** "Hades" actually stands for the god of the underworld. [[User:HenkJanVanTuyl|HenkJanVanTuyl]] 10:41, 31 December 2006 (UTC)<br />
<br />
== Links ==<br />
<br />
* NeilMitchell/WinHaskell - some thoughts from NeilMitchell about a lightweight GUI interface to a Haskell editor/interpreter.<br />
* HaskellIde - an older page discussing some ideas for a Haskell IDE<br />
* The [http://www.cse.unsw.edu.au/~dons/yi/ Yi] and [http://www.cse.unsw.edu.au/~dons/lambdabot/ lambdabot] source code - useful for understanding the dynamic plugin architecture.<br />
* The [http://www.scannedinavian.org/YiWiki Yi wiki] also has some documentation</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=99_questions/1_to_10&diff=940899 questions/1 to 102006-12-14T21:32:46Z<p>HenkJanVanTuyl: </p>
<hr />
<div>[[99_Haskell_exercises|Back to 99 Haskell exercises]]<br />
<br />
__NOTOC__<br />
<br />
These are Haskell translations of [http://www.ic.unicamp.br/~meidanis/courses/mc336/2006s2/funcional/L-99_Ninety-Nine_Lisp_Problems.html Ninety Nine Lisp Problems].<br />
<br />
If you want to work on one of these, put your name in the block so we know someone's working on it. Then, change n in your block to the appropriate problem number, and fill in the <Problem description>,<example in lisp>,<example in Haskell>,<solution in haskell> and <description of implementation> fields. <br />
<br />
== Problem 1 ==<br />
<br />
(*) Find the last box of a list.<br />
<br />
Example:<br />
<br />
<pre><br />
* (my-last '(a b c d))<br />
(D)<br />
</pre><br />
<br />
Example in Haskell:<br />
<br />
<haskell><br />
Prelude> myLast [1,2,3,4]<br />
[4]<br />
Prelude> myLast ['x','y','z']<br />
"z"<br />
</haskell><br />
<br />
Solution:<br />
<br />
<haskell><br />
myLast :: [a] -> [a]<br />
myLast [x] = [x]<br />
myLast (_:xs) = myLast xs<br />
</haskell><br />
<br />
Haskell also provides the function <hask>last</hask>.<br />
<br />
== Problem 2 ==<br />
<br />
(*) Find the last but one box of a list.<br />
<br />
Example:<br />
<br />
<pre><br />
* (my-but-last '(a b c d))<br />
(C D)<br />
</pre><br />
<br />
Example in Haskell:<br />
<br />
<haskell><br />
Prelude> myButLast [1,2,3,4]<br />
[3,4]<br />
Prelude> myButLast ['a'..'z']<br />
"yz"<br />
</haskell><br />
<br />
Solution:<br />
<br />
<haskell><br />
myButLast :: [a] -> [a]<br />
myButLast list = drop ((length list) - 2) list<br />
</haskell><br />
<br />
This simply drops all the but last two elements of a list.<br />
<br />
Some other options:<br />
<haskell><br />
myButLast = reverse . take 2 . reverse<br />
</haskell><br />
or<br />
<haskell><br />
myButLast = last . liftM2 (zipWith const) tails (drop 1)<br />
</haskell><br />
<br />
Remark:<br />
The Lisp solution is actually wrong, it should not be the last two elements; a correct Haskell solution is:<br />
<haskell><br />
myButLast = last . init<br />
Prelude> myButLast ['a'..'z']<br />
'y'<br />
</haskell><br />
See also the solution to problem 2 in the Prolog list.<br />
<br />
== Problem 3 ==<br />
<br />
(*) Find the K'th element of a list. The first element in the list is number 1.<br />
<br />
Example:<br />
<br />
<pre><br />
* (element-at '(a b c d e) 3)<br />
C<br />
</pre><br />
<br />
Example in Haskell:<br />
<br />
<haskell><br />
Prelude> elementAt [1,2,3] 2<br />
2<br />
Prelude> elementAt "haskell" 5<br />
'e'<br />
</haskell><br />
<br />
Solution:<br />
<br />
This is (almost) the infix operator !! in Prelude, which is defined as:<br />
<br />
<haskell><br />
(!!) :: [a] -> Int -> a<br />
(x:_) !! 0 = x<br />
(_:xs) !! n = xs !! (n-1)<br />
</haskell><br />
<br />
Except this doesn't quite work, because !! is zero-indexed, and element-at should be one-indexed. So:<br />
<br />
<haskell><br />
elementAt :: [a] -> Int -> a<br />
elementAt list i = list !! (i-1)<br />
</haskell><br />
<br />
== Problem 4 ==<br />
<br />
(*) Find the number of elements of a list.<br />
<br />
Example in Haskell:<br />
<br />
<haskell><br />
Prelude> length [123, 456, 789]<br />
3<br />
Prelude> length "Hello, world!"<br />
13<br />
</haskell><br />
<br />
Solution:<br />
<br />
<haskell><br />
length :: [a] -> Int<br />
length [] = 0<br />
length (_:l) = 1 + length l<br />
</haskell><br />
<br />
This function is defined in Prelude.<br />
<br />
== Problem 5 ==<br />
<br />
(*) Reverse a list.<br />
<br />
Example in Haskell:<br />
<br />
<haskell><br />
Prelude> reverse "A man, a plan, a canal, panama!"<br />
"!amanap ,lanac a ,nalp a ,nam A"<br />
Prelude> reverse [1,2,3,4]<br />
[4,3,2,1]<br />
</haskell><br />
<br />
Solution: (defined in Prelude)<br />
<br />
<haskell><br />
reverse :: [a] -> [a]<br />
reverse = foldl (flip (:)) []<br />
</haskell><br />
<br />
The standard definition is concise, but not very readable. Another way to define reverse is:<br />
<br />
<haskell><br />
reverse :: [a] -> [a]<br />
reverse [] = []<br />
reverse (x:xs) = reverse xs ++ [x]<br />
</haskell><br />
<br />
== Problem 6 ==<br />
<br />
(*) Find out whether a list is a palindrome. A palindrome can be read forward or backward; e.g. (x a m a x).<br />
<br />
Example in Haskell:<br />
<br />
<haskell><br />
*Main> isPalindrome [1,2,3]<br />
False<br />
*Main> isPalindrome "madamimadam"<br />
True<br />
*Main> isPalindrome [1,2,4,8,16,8,4,2,1]<br />
True<br />
</haskell><br />
<br />
Solution:<br />
<br />
<haskell><br />
isPalindrome :: (Eq a) => [a] -> Bool<br />
isPalindrome xs = xs == (reverse xs)<br />
</haskell><br />
<br />
== Problem 7 ==<br />
<br />
(**) Flatten a nested list structure.<br />
<br />
Transform a list, possibly holding lists as elements into a `flat' list by replacing each list with its elements (recursively).<br />
<br />
Example:<br />
<br />
<pre><br />
* (my-flatten '(a (b (c d) e)))<br />
(A B C D E)<br />
</pre><br />
<br />
Example in Haskell:<br />
<br />
<haskell><br />
*Main> flatten (Elem 5)<br />
[5]<br />
*Main> flatten (List [Elem 1, List [Elem 2, List [Elem 3, Elem 4], Elem 5]])<br />
[1,2,3,4,5]<br />
*Main> flatten (List [])<br />
[]<br />
</haskell><br />
<br />
Solution:<br />
<br />
<haskell><br />
data NestedList a = Elem a | List [NestedList a]<br />
<br />
flatten :: NestedList a -> [a]<br />
flatten (Elem x) = [x]<br />
flatten (List x) = concatMap flatten x<br />
</haskell><br />
<br />
We have to defined a new data type, because lists in Haskell are homogeneous.<br />
[1, [2, [3, 4], 5]] is a type error. Therefore, we must have a way of<br />
representing a list that may (or may not) be nested.<br />
<br />
Our NestedList datatype is either a single element of some type (Elem a), or a<br />
list of NestedLists of the same type. (List [NestedList a]). <br />
<br />
== Problem 8 ==<br />
<br />
(**) Eliminate consecutive duplicates of list elements.<br />
<br />
If a list contains repeated elements they should be replaced with a single copy of the element. The order of the elements should not be changed.<br />
<br />
<pre><br />
Example:<br />
* (compress '(a a a a b c c a a d e e e e))<br />
(A B C A D E)<br />
<br />
Example in Haskell:<br />
*Main> compress ['a','a','a','a','b','c','c','a','a','d','e','e','e','e']<br />
['a','b','c','a','d','e']<br />
</pre><br />
<br />
Solution:<br />
<haskell><br />
compress :: Eq a => [a] -> [a]<br />
compress = map head . group<br />
</haskell><br />
<br />
We simply group equal values together (group), then take the head of each. <br />
Note that (with GHC) we must give an explicit type to ''compress'' otherwise we get:<br />
<br />
<haskell><br />
Ambiguous type variable `a' in the constraint:<br />
`Eq a'<br />
arising from use of `group' <br />
Possible cause: the monomorphism restriction applied to the following:<br />
compress :: [a] -> [a]<br />
Probable fix: give these definition(s) an explicit type signature<br />
or use -fno-monomorphism-restriction<br />
</haskell><br />
<br />
We can circumvent the monomorphism restriction by writing ''compress'' this way (See: section 4.5.4 of [http://haskell.org/onlinereport the report]):<br />
<br />
<haskell>compress xs = map head $ group xs</haskell><br />
<br />
== Problem 9 ==<br />
<br />
(**) Pack consecutive duplicates of list elements into sublists.<br />
If a list contains repeated elements they should be placed in separate sublists.<br />
<br />
<br />
<br />
<pre><br />
Example:<br />
* (pack '(a a a a b c c a a d e e e e))<br />
((A A A A) (B) (C C) (A A) (D) (E E E E))<br />
<example in lisp><br />
<br />
Example in Haskell:<br />
</pre><br />
<br />
Solution:<br />
<haskell><br />
group (x:xs) = let (first,rest) = span (==x) xs<br />
in (x:first) : group rest<br />
group [] = []<br />
</haskell><br />
<br />
'group' is also in the Prelude, here's an implementation using 'span'.<br />
<br />
== Problem 10 ==<br />
<br />
(*) Run-length encoding of a list.<br />
Use the result of problem P09 to implement the so-called run-length encoding data compression method. Consecutive duplicates of elements are encoded as lists (N E) where N is the number of duplicates of the element E.<br />
<br />
Example:<br />
<pre><br />
* (encode '(a a a a b c c a a d e e e e))<br />
((4 A) (1 B) (2 C) (2 A) (1 D)(4 E))<br />
</pre><br />
<br />
Example in Haskell:<br />
<pre><br />
encode "aaaabccaadeeee"<br />
[(4,'a'),(1,'b'),(2,'c'),(2,'a'),(1,'d'),(4,'e')]<br />
</pre><br />
<br />
Solution:<br />
<haskell><br />
encode xs = map (\x -> (length x,head x)) (group xs)<br />
</haskell><br />
<br />
Or writing it [[Pointfree]]:<br />
<br />
<haskell><br />
encode :: Eq a => [a] -> [(Int, a)]<br />
encode = map (\x -> (length x, head x)) . group<br />
</haskell><br />
<br />
Or (ab)using the "&&&" arrow operator for tuples:<br />
<br />
<haskell><br />
encode :: Eq a => [a] -> [(Int, a)]<br />
encode xs = map (length &&& head) $ group xs<br />
</haskell> <br />
[[Category:Tutorials]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Simple_Unix_tools&diff=9012Simple Unix tools2006-12-10T18:59:17Z<p>HenkJanVanTuyl: </p>
<hr />
<div>Simple unix tools written in Haskell. <br />
<br />
This is intended as a beginners tutorial for learning Haskell from a <br />
"Lets just solve things already!" point of view. The examples should<br />
help give a flavour of the beauty and expressiveness of Haskell<br />
programming.<br />
<br />
<haskell><br />
--<br />
-- Some unix-like tools written in simple, clean Haskell<br />
--<br />
--<br />
<br />
import Data.List<br />
import Data.Char<br />
import System.IO<br />
import Text.Printf<br />
<br />
-- <br />
-- First, two helpers<br />
--<br />
io f = interact (unlines . f . lines)<br />
showln = (++ "\n") . show<br />
<br />
--<br />
-- The 'cat' program<br />
--<br />
cat = interact id<br />
<br />
--<br />
-- Sort a file<br />
--<br />
sort' = io sort<br />
<br />
--<br />
-- remove duplicate lines from a file (like uniq)<br />
--<br />
uniq = io nub<br />
<br />
-- <br />
-- repeat the input file infinitely <br />
--<br />
rpt = interact cycle<br />
<br />
--<br />
-- Return the head -10 line of a file<br />
--<br />
take' = io (take 10)<br />
<br />
--<br />
-- Remove the first 10 lines of a file<br />
--<br />
drop' = io (drop 10)<br />
<br />
--<br />
-- Return the head -1 line of a file<br />
--<br />
head' = io (return . head)<br />
<br />
--<br />
-- Return the tail -1 line of a file<br />
--<br />
tail' = io (return . last)<br />
<br />
-- <br />
-- Reverse lines in a file (tac)<br />
--<br />
tac = io reverse<br />
<br />
--<br />
-- Reverse characters on each line (rev)<br />
--<br />
rev = io (map reverse)<br />
<br />
--<br />
-- Reverse words on each line<br />
--<br />
revw = io $ map (unwords. reverse . words)<br />
<br />
--<br />
-- Count number of characters in a file (like wc -c)<br />
--<br />
wc_c = interact (showln . length)<br />
<br />
--<br />
-- Count number of lines in a file, like wc -l<br />
--<br />
wc_l = interact (showln . length . lines)<br />
<br />
--<br />
-- Count number of words in a file (like wc -w)<br />
--<br />
wc_w = interact (showln . length . words)<br />
<br />
--<br />
-- double space a file<br />
--<br />
space = io (intersperse "")<br />
<br />
-- <br />
-- undo double space<br />
--<br />
unspace = io $ filter (not.null)<br />
<br />
--<br />
-- remove the first occurence of the line "str"<br />
--<br />
remove = io (delete "str")<br />
<br />
--<br />
-- make a file all upper case<br />
--<br />
upper = interact (map toUpper)<br />
<br />
--<br />
-- remove leading space from each line<br />
--<br />
clean = io $ map (dropWhile isSpace)<br />
<br />
--<br />
-- remove trailing whitespace<br />
--<br />
clean' = io (map f)<br />
where f = reverse . dropWhile isSpace . reverse<br />
<br />
--<br />
-- delete leading and trailing whitespace<br />
--<br />
clean'' = io $ map (f . f)<br />
where f = reverse . dropWhile isSpace<br />
<br />
--<br />
-- insert blank space at beginning of each line<br />
--<br />
blank = io $ map (s ++)<br />
where s = replicate 8 ' '<br />
<br />
--<br />
-- join lines of a file<br />
--<br />
join = io (return . concat)<br />
<br />
--<br />
-- Translate the letter 'e' to '*', like tr 'e' '*' (or y// in sed)<br />
--<br />
y = interact (map f)<br />
where f 'e' = '*'<br />
f c = c<br />
--<br />
-- Filter the letter 'e' from a file, like tr -d 'e'<br />
--<br />
tr = interact $ filter (/= 'e')<br />
<br />
--<br />
-- grep lines matching "^foo" from a file<br />
--<br />
grep = io $ filter (isPrefixOf "foo")<br />
<br />
--<br />
-- grep lines that don't match "^foo" (grep -v)<br />
--<br />
grep_v = io $ filter (not . isPrefixOf "foo")<br />
<br />
--<br />
-- number each line of a file<br />
--<br />
num = io $ zipWith (printf "%3d %s") [(1::Int)..]<br />
<br />
--<br />
-- Compute a simple cksum of a file<br />
--<br />
cksum = interact $ showln . foldl' k 5381<br />
where k h c = h * 33 + ord c<br />
<br />
</haskell><br />
<br />
<br />
'''Where to now?'''<br />
<br />
* [[Haskell|Haskell.org]]<br />
* The Haskell standard [http://www.cse.unsw.edu.au/~dons/data/List.html list library], with [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html docs]<br />
* Alternative [[Wc|implementations]] of the wc program<br />
* Learn how to [[Introduction_to_QuickCheck|test Haskell code]]<br />
* [[Example_code|More]] Haskell code<br />
* Haskell for [[Libraries_and_tools/Operating_system#Shell|shell scripting]]<br />
* Export list functions to the shell with [http://www.cse.unsw.edu.au/~dons/h4sh.html h4sh]<br />
<br />
[[Category:Tutorials]]<br />
[[Category:Code]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Yhc/Options&diff=8878Yhc/Options2006-12-04T22:20:59Z<p>HenkJanVanTuyl: Added description of environment variables YHC_BASE_PATH and PATH</p>
<hr />
<div>Command syntax:<br />
yhc [options] file<br />
<br />
file: Name of the source file to compile, i.e. main.hs<br />
options: a list of options<br />
-flag turn on an option<br />
-noflag turn off an option<br />
-flag value pass a value as the flag<br />
<br />
Path Options:<br />
-I -includes str Include directories<br />
-P -preludes str Prelude directories<br />
-d -dst str destination path for generated bytecode files (default=.)<br />
-i -hidst str destinated path for generated .hi file (default=.)<br />
-w -wrapdst str destination path for generated FFI wrapper (default=.)<br />
-hide hide object files (default=off)<br />
<br />
Generation Options:<br />
-hat compile with Hat debugging support (default=off)<br />
-dotnet Generate .NET IL code (default=off)<br />
-W -genwrapper generate FFI wrapper (default=off)<br />
-hi-suffix str change the default ".hi" suffix (default=hi)<br />
-exportall export all identifiers from a module, despite what the <br />
export list says (default=off)<br />
<br />
Action Flags:<br />
-viewcore str View Core file (.ycr)<br />
-c -compile Compile one file only (default=off)<br />
<br />
Compilation Options:<br />
-redefine Don't complain if redefining an imported identifier <br />
(default=off)<br />
-unix Use unix file names (default=on)<br />
-unlit Unliterate the source code (default=off)<br />
-cpp Pre-process the file first (default=off)<br />
-prelude Keep prelude definitions in interface file (default=off)<br />
-lib Compiling a lib, don't complain if importing modules with <br />
names that differs from their filename. (default=off)<br />
-unifyhack Enable nasty type hack that's needed to make the prelude <br />
compile ... (default=off)<br />
<br />
Compliance Options:<br />
-underscore Enable H'98 underscore-is-lower-case (default=off)<br />
-puns Enable pre-98 named-field puns (default=on)<br />
-98 Haskell 98 compliance (default=off)<br />
<br />
Help Options:<br />
-v -version Show version information (default=off)<br />
-? -help Show all options and useage information (default=off)<br />
<br />
Core Options:<br />
-core generate a .ycr binary file (default=off)<br />
-showcore show the Core language (default=off)<br />
-linkcore generate a linked .yca binary file (default=off)<br />
<br />
Debug Options:<br />
-lex show lexical input (default=off)<br />
-parse show syntax tree after parser (default=off)<br />
-need show need table before import (default=off)<br />
-ineed show need table after import (default=off)<br />
-irename show rename table after import (default=off)<br />
-iineed show need table between all import files (default=off)<br />
-iirename show rename table between all imports (default=off)<br />
-rename show syntax tree after rename (default=off)<br />
-derive show syntax tree after derive (default=off)<br />
-remove show syntax tree after fields are removed (translated into <br />
selectors) (default=off)<br />
-scc show syntax tree after splitting into strongly connected <br />
groups (default=off)<br />
-type show syntax tree after type check (default=off)<br />
-fixsyntax show syntax tree after removing newtype constructors and <br />
fixing Class.Type.metod (default=off)<br />
-lift show syntax tree after lambda lifting (default=off)<br />
-case show stg tree after simplification of patterns <br />
(default=off)<br />
-prim show stg tree after inserting primitive functions <br />
(default=off)<br />
-bcodecompile show B code after compile (default=off)<br />
-bcodemem show B code after heap usage analysis (default=off)<br />
-bcodeflatten show B code after flattening (default=off)<br />
-bcoderel show B code after converting to relative jumps <br />
(default=off)<br />
-keepcase Don't lift case, we fix those later (default=off)<br />
-arity show stg tree after arity (default=off)<br />
-ibound show symbol table after import (default=off)<br />
-iibound show symbol table between all import files (default=off)<br />
-rbound show symbol table after rename (default=off)<br />
-dbound show symbol table after derive (default=off)<br />
-pbound show symbol table after inserting primitive functions <br />
(default=off)<br />
-ebound show symbol table after extract (default=off)<br />
-tbound show symbol table after type check (default=off)<br />
-fsbound show symbol table after adding Class.Type.method info <br />
(default=off)<br />
-lbound show symbol table after lambda lifting (default=off)<br />
-cbound show symbol table after simplification of pattern <br />
(default=off)<br />
-abound show symbol table after only atoms in applications <br />
(default=off)<br />
-import print name of imported files (default=off)<br />
-depend print imported identifiers that are used (not even alpha <br />
yet) (default=off)<br />
-free show stg tree with explicitly free variables (default=off)<br />
-atom show stg tree after only atoms in applications <br />
(default=off)<br />
-funnames insert position and name of functions in the code <br />
(default=off)<br />
-ilex show lexical input (default=off)<br />
-report-imports show only imports actually used (default=off)<br />
-showtype report type of "main" (default=off)<br />
-showwidth num set width for showing intermediate program (default=80)<br />
-showindent num set indentation for nesting (default=2)<br />
-showqualified show qualified ids as far as possible (default=on)<br />
<br />
<br />
<br />
Environment variables:<br />
<br />
YHC_BASE_PATH The path to the Yhc compiler base directory<br />
PATH The search path is used to find the compiler,<br />
if YHC_BASE_PATH is not specified</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Yhc/Options&diff=8876Yhc/Options2006-12-04T21:34:20Z<p>HenkJanVanTuyl: Output from "yhc --help" inserted</p>
<hr />
<div>Command syntax:<br />
yhc [options] file<br />
<br />
file: Name of the source file to compile, i.e. main.hs<br />
options: a list of options<br />
-flag turn on an option<br />
-noflag turn off an option<br />
-flag value pass a value as the flag<br />
<br />
Path Options:<br />
-I -includes str Include directories<br />
-P -preludes str Prelude directories<br />
-d -dst str destination path for generated bytecode files (default=.)<br />
-i -hidst str destinated path for generated .hi file (default=.)<br />
-w -wrapdst str destination path for generated FFI wrapper (default=.)<br />
-hide hide object files (default=off)<br />
<br />
Generation Options:<br />
-hat compile with Hat debugging support (default=off)<br />
-dotnet Generate .NET IL code (default=off)<br />
-W -genwrapper generate FFI wrapper (default=off)<br />
-hi-suffix str change the default ".hi" suffix (default=hi)<br />
-exportall export all identifiers from a module, despite what the <br />
export list says (default=off)<br />
<br />
Action Flags:<br />
-viewcore str View Core file (.ycr)<br />
-c -compile Compile one file only (default=off)<br />
<br />
Compilation Options:<br />
-redefine Don't complain if redefining an imported identifier <br />
(default=off)<br />
-unix Use unix file names (default=on)<br />
-unlit Unliterate the source code (default=off)<br />
-cpp Pre-process the file first (default=off)<br />
-prelude Keep prelude definitions in interface file (default=off)<br />
-lib Compiling a lib, don't complain if importing modules with <br />
names that differs from their filename. (default=off)<br />
-unifyhack Enable nasty type hack that's needed to make the prelude <br />
compile ... (default=off)<br />
<br />
Compliance Options:<br />
-underscore Enable H'98 underscore-is-lower-case (default=off)<br />
-puns Enable pre-98 named-field puns (default=on)<br />
-98 Haskell 98 compliance (default=off)<br />
<br />
Help Options:<br />
-v -version Show version information (default=off)<br />
-? -help Show all options and useage information (default=off)<br />
<br />
Core Options:<br />
-core generate a .ycr binary file (default=off)<br />
-showcore show the Core language (default=off)<br />
-linkcore generate a linked .yca binary file (default=off)<br />
<br />
Debug Options:<br />
-lex show lexical input (default=off)<br />
-parse show syntax tree after parser (default=off)<br />
-need show need table before import (default=off)<br />
-ineed show need table after import (default=off)<br />
-irename show rename table after import (default=off)<br />
-iineed show need table between all import files (default=off)<br />
-iirename show rename table between all imports (default=off)<br />
-rename show syntax tree after rename (default=off)<br />
-derive show syntax tree after derive (default=off)<br />
-remove show syntax tree after fields are removed (translated into <br />
selectors) (default=off)<br />
-scc show syntax tree after splitting into strongly connected <br />
groups (default=off)<br />
-type show syntax tree after type check (default=off)<br />
-fixsyntax show syntax tree after removing newtype constructors and <br />
fixing Class.Type.metod (default=off)<br />
-lift show syntax tree after lambda lifting (default=off)<br />
-case show stg tree after simplification of patterns <br />
(default=off)<br />
-prim show stg tree after inserting primitive functions <br />
(default=off)<br />
-bcodecompile show B code after compile (default=off)<br />
-bcodemem show B code after heap usage analysis (default=off)<br />
-bcodeflatten show B code after flattening (default=off)<br />
-bcoderel show B code after converting to relative jumps <br />
(default=off)<br />
-keepcase Don't lift case, we fix those later (default=off)<br />
-arity show stg tree after arity (default=off)<br />
-ibound show symbol table after import (default=off)<br />
-iibound show symbol table between all import files (default=off)<br />
-rbound show symbol table after rename (default=off)<br />
-dbound show symbol table after derive (default=off)<br />
-pbound show symbol table after inserting primitive functions <br />
(default=off)<br />
-ebound show symbol table after extract (default=off)<br />
-tbound show symbol table after type check (default=off)<br />
-fsbound show symbol table after adding Class.Type.method info <br />
(default=off)<br />
-lbound show symbol table after lambda lifting (default=off)<br />
-cbound show symbol table after simplification of pattern <br />
(default=off)<br />
-abound show symbol table after only atoms in applications <br />
(default=off)<br />
-import print name of imported files (default=off)<br />
-depend print imported identifiers that are used (not even alpha <br />
yet) (default=off)<br />
-free show stg tree with explicitly free variables (default=off)<br />
-atom show stg tree after only atoms in applications <br />
(default=off)<br />
-funnames insert position and name of functions in the code <br />
(default=off)<br />
-ilex show lexical input (default=off)<br />
-report-imports show only imports actually used (default=off)<br />
-showtype report type of "main" (default=off)<br />
-showwidth num set width for showing intermediate program (default=80)<br />
-showindent num set indentation for nesting (default=2)<br />
-showqualified show qualified ids as far as possible (default=on)</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Playing_by_the_rules&diff=8636Playing by the rules2006-11-23T18:46:47Z<p>HenkJanVanTuyl: </p>
<hr />
<div>GHC's term rewriting engine ([http://haskell.org/haskellwiki/GHC/Using_Rules rewrite rules]).<br />
provide a playground for all sorts of user-defined optimisations and<br />
tweaks to Haskell code. This page collects examples of the use of<br />
rewrite rules. There is more information available on the GHC [http://hackage.haskell.org/trac/ghc/wiki/RewriteRules rewrite rules] page.<br />
<br />
== reverse . reverse = id ==<br />
<br />
The following question was asked in [[IRC channel|#haskell]]:<br />
<br />
15:40 Excedrin> why doesn't 'head $ reverse $ reverse [1..]' work in Haskell?<br />
<br />
The answer is: it does work, if you tell the compiler that:<br />
<br />
<math><br />
reverse~.~reverse~=~id<br />
</math><br />
<br />
Using the following rewrite rule:<br />
<br />
<haskell><br />
{-# RULES<br />
"reverse.reverse/id" reverse . reverse = id<br />
#-}<br />
<br />
main = print . head . reverse . reverse $ [1..]<br />
</haskell><br />
<br />
We can "optimise" this program, changing a non-terminating program:<br />
<haskell><br />
$ ./a.out<br />
^C<br />
</haskell><br />
<br />
into a much faster one:<br />
<br />
<haskell><br />
$ ghc -fglasgow-exts -ddump-simpl-stats A.hs<br />
5 PostInlineUnconditionally<br />
4 UnfoldingDone<br />
1 RuleFired<br />
1 reverse.reverse/id<br />
7 BetaReduction<br />
3 SimplifierDone<br />
<br />
$ ./a.out<br />
1<br />
</haskell><br />
<br />
As can be seen here, rewrite rules let you teach the compiler about<br />
interesting algebraic properties you code satisfies, that it can't find<br />
on its own.<br />
<br />
'''Note:''' In general it's a bad idea for rules to change the semantics (meaning) of your code in such a way. Consider someone using <tt>reverse . reverse</tt> to demand strictness of an IO action, this code would no longer work with the above rule.<br />
<br />
== Fast ByteString construction ==<br />
<br />
Another technique is to use rewrite rules to remove intermediate lists<br />
when building bytestrings from literal string constants. The problem is<br />
that string constants must be converted from a [Char] to a ByteString,<br />
to use literal strings as ByteStrings. This is highly inefficient, when<br />
GHC already allocates string constants as unboxed byte arrays. We can<br />
bypass the [Char] conversion using rewrite rules.<br />
<br />
Supposing we want to use a ByteString literal:<br />
<br />
<haskell><br />
import qualified Data.ByteString.Char8 as C<br />
<br />
mystring = C.pack "rewrite rules are fun!"<br />
main = C.putStrLn mystring<br />
</haskell><br />
<br />
and it is compiled by GHC to:<br />
<br />
<haskell><br />
mystring_rDR = Data.ByteString.Char8.pack (GHC.Base.unpackCString# "rewrite rules are fun!"#)<br />
<br />
Main.main = Data.ByteString.putStrLn mystring_rDR<br />
<br />
:Main.main = GHC.TopHandler.runMainIO @ () Main.main<br />
</haskell><br />
<br />
Now, the string literal has been packed by GHC for us into a Addr#, pointing<br />
to a proper C string. Now, we'd like to remove that intermediate<br />
unpackCString#, and just pack the string literal straight into a ByteString,<br />
avoiding an intermediate list.<br />
<br />
So we add a rewrite rule:<br />
<br />
<haskell><br />
{-# RULES<br />
"pack/packAddress" forall s . pack (unpackCString# s) = B.packAddress s<br />
#-}<br />
</haskell><br />
<br />
Now, when compiled with -O -fglasgow-exts, we get:<br />
<br />
<haskell><br />
1 RuleFired<br />
1 FPS pack/packAddress<br />
</haskell><br />
<br />
and the code is transformed as follows:<br />
<br />
<haskell><br />
mystring = Data.ByteString.Base.packAddress "rewrite rules are fun!"<br />
</haskell><br />
<br />
to<br />
<br />
<haskell><br />
mystring = Data.ByteString.Char8.pack (GHC.Base.unpackCString# "rewrite rules are fun<br />
</haskell><br />
<br />
and then the rewrite rule kicks in, and we construct a new ByteString directly<br />
from the Addr#, via a call to strlen to get the length:<br />
<br />
<haskell><br />
mystring =<br />
let addr#_a146 :: GHC.Prim.Addr#<br />
addr#_a146 = "rewrite rules are fun!"#<br />
in case Data.ByteString.Base.$wccall addr#_a146 s2#_a1Q8 of<br />
(# ds3_a1Re, ds4_a1Rf #) -><br />
Data.ByteString.Base.PS addr#_a146<br />
(GHC.ForeignPtr.PlainForeignPtr var#_a1Q9)<br />
0<br />
(GHC.Prim.word2Int# ds4_a1Rf)<br />
</haskell><br />
<br />
''exactly'' what we'd like. So at runtime, this string will require only a call<br />
to strlen to build.<br />
<br />
== Locating errors ==<br />
<br />
Rewrite rules can ''almost'' be used to solve the infamous 'fromJust: Nothing'<br />
error message. Couldn't we just use rewrite rules to rewrite ''*transparently''<br />
all uses of fromJust to safeFromJust, tagging the call site with a location?<br />
To work this requires a few things to go right:<br />
<br />
* a rewrite rule<br />
* assertions<br />
* and rewrite rules firing before assertions are expanded<br />
<br />
Let's try this. Consider the program:<br />
<br />
<haskell><br />
1 import qualified Data.Map as M<br />
2 import Data.Maybe<br />
3<br />
4 main = print f<br />
5<br />
6 f = let m = M.fromList<br />
7 [(1,"1")<br />
8 ,(2,"2")<br />
9 ,(3,"3")]<br />
10 s = M.lookup 4 m<br />
11 in fromJust s<br />
</haskell><br />
<br />
When we run it we get the not so useful error:<br />
<br />
$ ./A<br />
A: Maybe.fromJust: Nothing<br />
<br />
Ok, so we have a few tricks for locating this, using<br />
[http://www.cse.unsw.edu.au/~dons/loch.html LocH], we can catch an assertion<br />
failure, but we have to insert the assertion by hand:<br />
<br />
<haskell><br />
1 import Debug.Trace.Location<br />
2 import qualified Data.Map as M<br />
3 import Data.Maybe<br />
4<br />
5 main = do print f<br />
6<br />
7 f = let m = M.fromList<br />
8 [(1,"1")<br />
9 ,(2,"2")<br />
10 ,(3,"3")]<br />
11 s = M.lookup 4 m<br />
12 in safeFromJust assert s<br />
13<br />
14 safeFromJust a = check a . fromJust<br />
</haskell><br />
<br />
Which correctly identifies the call site:<br />
<br />
$ ./A<br />
A: A.hs:12:20-25: Maybe.fromJust: Nothing<br />
<br />
Now, this approach is a little fragile. 'assert' is only respected by GHC if -O<br />
is ''not'' on, so if we happened to try this trick with -O, we'd get:<br />
<br />
$ ./A<br />
A: Debug.Trace.Location.failure<br />
<br />
So lesson one: you have to do the bug hunting with -Onot.<br />
<br />
Currently there's -fignore-asserts for turning off assertions, but no flag for<br />
turning them on with -O, Simon, could this be fixed? Could we get a<br />
-frespect-asserts that works even with -O ?<br />
<br />
Ok, assuming this assert trick is used, can we get the compiler to insert the<br />
asserts for us? If so, this would be a great advantage, you'd just be able to<br />
switch on a flag, or import a debugging module, and your fromJusts would be<br />
transparently rewritten. With rewrite rules we do just this!<br />
<br />
So, to our initial unsafe use of fromJust, we add a rewrite rule:<br />
<br />
<haskell><br />
--<br />
-- rewrite fromJust to a located version, and hope that GHC expands<br />
-- 'assert' after the rule fires..<br />
--<br />
{-# RULES<br />
"located fromJust" fromJust = check assert . myFromJust<br />
#-}<br />
</haskell><br />
<br />
This just tells the compiler to replace every occurence of fromJust with a<br />
assertion-throwing fromJust, should it fail. We have to use myFromJust here, to<br />
avoid rule recursion.<br />
<br />
<haskell><br />
--<br />
-- Inlined to avoid recursion in the rule:<br />
--<br />
myFromJust :: Maybe a -> a<br />
myFromJust Nothing = error "Maybe.fromJust: Nothing" -- yuck<br />
myFromJust (Just x) = x<br />
</haskell><br />
<br />
Ok, so can we get ghc to rewrite fromJust to the safe fromJust magicaly?<br />
<br />
$ ghc --make -Onot A.hs -fglasgow-exts -ddump-simpl-stats<br />
[1 of 1] Compiling Main ( A.hs, A.o )<br />
1 RuleFired<br />
1 located fromJust<br />
Linking A ...<br />
<br />
Yes, the rule fired! GHC *did* rewrite our fromJust to a more useful fromJust.<br />
Running it:<br />
<br />
<haskell><br />
$ ./A<br />
A: A.hs:19:36-41: Maybe.fromJust: Nothing<br />
</haskell><br />
<br />
Looks good! But that is deceiving: the assert was expanded before the rule<br />
fired, and refers to the rewrite rule source line (line 19), not the fromJust<br />
call site (line 12). Now if we could just have the 'assert' token inserted<br />
into the AST before it was expanded, we'd be home and dry. Could this be done<br />
with TH? Or could we arrange for asserts in rewrite rules not to be expanded<br />
till later?<br />
<br />
Note that this is still a useful technique, we can rewrite head/fromJust/... to<br />
some other possibly more useful message. And if we can constrain the rule to fire<br />
in only particular modules, we may be able to narrow down the bug, just by<br />
turning on a rule. For example, adding:<br />
<br />
<haskell><br />
{-# RULES<br />
"located fromJust" fromJust = safeFromJust<br />
#-}<br />
<br />
safeFromJust s = case s of<br />
Nothing -> "safeFromJust: failed with Nothing. Ouch"<br />
Just x -> x<br />
</haskell><br />
<br />
will produce:<br />
<br />
<haskell><br />
$ ./A<br />
"safeFromJust: failed with Nothing. Ouch"<br />
</haskell><br />
<br />
So rewrite rules can be used to transparently alter uses of partial functions<br />
like head and fromJust.<br />
<br />
== Fusion and deforestation ==<br />
<br />
By far the most elaborate use of compiler rewrite rules is to perform<br />
automatic deforestation of intermediate data structures. This is<br />
documented in a number of [[Research_papers/Compilation#Fusion_and_deforestation|research papers]].<br />
Note that also for this application field of rewrite rules the above mentioned problem of potentially changing the semantics of programs occurs. Read further under [[Correctness of short cut fusion|correctness of short cut fusion]].<br />
<br />
== Symbolic differentiation ==<br />
<br />
I like to demonstrate to people, that GHC has already built-in a kind of Computer Algebra system.<br />
The optimizer can be abused to differentiate expressions symbolically.<br />
Unfortunately it cannot be forced to do so.<br />
That's why, some, better say: too many, expressions are not derived.<br />
<br />
* http://www.haskell.org/pipermail/haskell-cafe/2005-March/009489.html<br />
* http://darcs.haskell.org/htam/src/ExploreHaskell/SymbolicDifferentiation.hs<br />
<br />
<br />
[[Category:Tutorials]]<br />
[[Category:Mathematics]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Applications_and_libraries/Compilers_and_interpreters&diff=7639Applications and libraries/Compilers and interpreters2006-10-30T21:46:00Z<p>HenkJanVanTuyl: Corrected the link to PolyP</p>
<hr />
<div>Haskell, with its support for pattern matching on data structures,<br />
generic structure traversals, and expressive type system, is popular for<br />
implementing compilers and intepreters. Here's a selection of languages<br />
implemented in Haskell.<br />
<br />
=Large languages=<br />
<br />
==Haskell==<br />
<br />
;[http://haskell.org/ghc GHC]<br />
:GHC, The Glasgow Haskell Compiler, is written in Haskell<br />
<br />
;[[Yhc]]<br />
:Yhc, The York Haskell Compiler, is written in Haskell<br />
<br />
;[http://repetae.net/computer/jhc/ Jhc]<br />
:Jhc is a Haskell compiler which aims to produce the most efficient programs possible via whole program analysis<br />
<br />
;[http://haskell.org/nhc98 nhc98]<br />
:A compiler for Haskell 98, written in Haskell<br />
<br />
;[http://www.csg.lcs.mit.edu/projects/languages/ph.shtml pH]<br />
:A parallel version of Haskell from MIT.<br />
<br />
==Perl==<br />
<br />
;[http://pugscode.org Pugs]<br />
:Pugs is an implementation of Perl 6, written in Haskell. It aims to implement the full Perl6 specification.<br />
<br />
==Ruby==<br />
<br />
;[http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/7f3c1fd7d6c906aa RType]<br />
:RType is a Ruby interpreter written in Haskell<br />
<br />
==Scheme==<br />
<br />
;[http://halogen.note.amherst.edu/~jdtang/scheme_in_48/tutorial/overview.html Write Yourself a Scheme in 48 Hours]<br />
:A Haskell Tutorial.<br />
<br />
== Emacs Lisp ==<br />
;[http://www.codersbase.com/index.php/helisp Helisp]<br />
:The beginnings of an Emacs lisp compiler/interpreter.<br />
<br />
==Epigram==<br />
<br />
;[http://www.e-pig.org/ Epigram]<br />
:Epigram is a prototype dependently typed functional programming language<br />
<br />
==Curry==<br />
<br />
;[http://danae.uni-muenster.de/~lux/curry/ The M&uuml;nster Curry Compiler]<br />
:A native code compiler for the declarative multi-paradigm language Curry, written in Haskell<br />
<br />
==Bluespec==<br />
;[http://www.bluespec.com Bluespec]<br />
:A compiler for a hardware description language translating a Haskell-like (but with System Verilog syntax these days) language to Verilog.<br />
<br />
==Cayenne==<br />
;[http://www.depedent-types.org/ Cayenne]<br />
:A compiler for a Haskell-like language with depedent types.<br />
<br />
==Agda==<br />
;[http://www.cs.chalmers.se/~catarina/agda/ Agda]<br />
:A Cayenne-like programming language and proof assistant.<br />
<br />
==Chameleon==<br />
;[http://www.comp.nus.edu.sg/~sulzmann/chameleon/ Chameleon]<br />
:A Haskell-style language which implements the ideas described in a ``A Theory of Overloading``<br />
<br />
==Data Field Haskell==<br />
;[http://www.mrtc.mdh.se/projects/DFH/ Data Field Haskell]<br />
:A dialect of the functional programming language Haskell that provides an instance of data fields<br />
<br />
==Eden==<br />
;[http://www.mathematik.uni-marburg.de/~eden/ Eden]<br />
:A Haskell dialect for parallel programming<br />
<br />
==Generic Haskell==<br />
;[http://generic-haskell.org/ Generic Haskell]<br />
:An extension of Haskell that supports generic programming<br />
<br />
==PolyP==<br />
;[http://www.cs.chalmers.se/~patrikj/poly/polyp/ PolyP]<br />
:A polytypic programming language<br />
<br />
==Helium==<br />
;[http://www.cs.uu.nl/helium/ Helium]<br />
:A Haskell subset for educational purposes<br />
<br />
<br />
<br />
=Small languages=<br />
<br />
==Baskell==<br />
;[http://www.cs.mu.oz.au/~bjpop/code.html Baskell]<br />
:An interpreter for a small functional programming language. Supports strict and non-strict evaluation, and type inference. Useful for teaching purposes.<br />
<br />
==Unlambda==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/scripts/Unlambda.hs Unlambda.hs]<br />
:An implementation of unlambda in Haskell<br />
<br />
==BF==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/scripts/BF.hs BF.hs]<br />
:An implementation of BF in Haskell<br />
<br />
<br />
==Untyped lambda calculus==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/Plugin/Lambda/ LMEngine]<br />
:An implementation of the untyped lambda calculus<br />
<br />
<br />
<br />
{{LibrariesPage}}</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Applications_and_libraries/Compilers_and_interpreters&diff=7636Applications and libraries/Compilers and interpreters2006-10-30T21:36:30Z<p>HenkJanVanTuyl: Removed Epigram, it was mentioned twice</p>
<hr />
<div>Haskell, with its support for pattern matching on data structures,<br />
generic structure traversals, and expressive type system, is popular for<br />
implementing compilers and intepreters. Here's a selection of languages<br />
implemented in Haskell.<br />
<br />
=Large languages=<br />
<br />
==Haskell==<br />
<br />
;[http://haskell.org/ghc GHC]<br />
:GHC, The Glasgow Haskell Compiler, is written in Haskell<br />
<br />
;[[Yhc]]<br />
:Yhc, The York Haskell Compiler, is written in Haskell<br />
<br />
;[http://repetae.net/computer/jhc/ Jhc]<br />
:Jhc is a Haskell compiler which aims to produce the most efficient programs possible via whole program analysis<br />
<br />
;[http://haskell.org/nhc98 nhc98]<br />
:A compiler for Haskell 98, written in Haskell<br />
<br />
;[http://www.csg.lcs.mit.edu/projects/languages/ph.shtml pH]<br />
:A parallel version of Haskell from MIT.<br />
<br />
==Perl==<br />
<br />
;[http://pugscode.org Pugs]<br />
:Pugs is an implementation of Perl 6, written in Haskell. It aims to implement the full Perl6 specification.<br />
<br />
==Ruby==<br />
<br />
;[http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/7f3c1fd7d6c906aa RType]<br />
:RType is a Ruby interpreter written in Haskell<br />
<br />
==Scheme==<br />
<br />
;[http://halogen.note.amherst.edu/~jdtang/scheme_in_48/tutorial/overview.html Write Yourself a Scheme in 48 Hours]<br />
:A Haskell Tutorial.<br />
<br />
== Emacs Lisp ==<br />
;[http://www.codersbase.com/index.php/helisp Helisp]<br />
:The beginnings of an Emacs lisp compiler/interpreter.<br />
<br />
==Epigram==<br />
<br />
;[http://www.e-pig.org/ Epigram]<br />
:Epigram is a prototype dependently typed functional programming language<br />
<br />
==Curry==<br />
<br />
;[http://danae.uni-muenster.de/~lux/curry/ The M&uuml;nster Curry Compiler]<br />
:A native code compiler for the declarative multi-paradigm language Curry, written in Haskell<br />
<br />
==Bluespec==<br />
;[http://www.bluespec.com Bluespec]<br />
:A compiler for a hardware description language translating a Haskell-like (but with System Verilog syntax these days) language to Verilog.<br />
<br />
==Cayenne==<br />
;[http://www.depedent-types.org/ Cayenne]<br />
:A compiler for a Haskell-like language with depedent types.<br />
<br />
==Agda==<br />
;[http://www.cs.chalmers.se/~catarina/agda/ Agda]<br />
:A Cayenne-like programming language and proof assistant.<br />
<br />
==Chameleon==<br />
;[http://www.comp.nus.edu.sg/~sulzmann/chameleon/ Chameleon]<br />
:A Haskell-style language which implements the ideas described in a ``A Theory of Overloading``<br />
<br />
==Data Field Haskell==<br />
;[http://www.mrtc.mdh.se/projects/DFH/ Data Field Haskell]<br />
:A dialect of the functional programming language Haskell that provides an instance of data fields<br />
<br />
==Eden==<br />
;[http://www.mathematik.uni-marburg.de/~eden/ Eden]<br />
:A Haskell dialect for parallel programming<br />
<br />
==Generic Haskell==<br />
;[http://generic-haskell.org/ Generic Haskell]<br />
:An extension of Haskell that supports generic programming<br />
<br />
==PolyP==<br />
;[http://generic-haskell.org/ PolyP]<br />
:A polytypic programming language<br />
<br />
==Helium==<br />
;[http://www.cs.uu.nl/helium/ Helium]<br />
:A Haskell subset for educational purposes<br />
<br />
<br />
<br />
=Small languages=<br />
<br />
==Baskell==<br />
;[http://www.cs.mu.oz.au/~bjpop/code.html Baskell]<br />
:An interpreter for a small functional programming language. Supports strict and non-strict evaluation, and type inference. Useful for teaching purposes.<br />
<br />
==Unlambda==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/scripts/Unlambda.hs Unlambda.hs]<br />
:An implementation of unlambda in Haskell<br />
<br />
==BF==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/scripts/BF.hs BF.hs]<br />
:An implementation of BF in Haskell<br />
<br />
<br />
==Untyped lambda calculus==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/Plugin/Lambda/ LMEngine]<br />
:An implementation of the untyped lambda calculus<br />
<br />
<br />
<br />
{{LibrariesPage}}</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Applications_and_libraries/Compilers_and_interpreters&diff=7634Applications and libraries/Compilers and interpreters2006-10-30T20:52:17Z<p>HenkJanVanTuyl: Added Chameleon, Data Field Haskell, Eden, Generic Haskell, PolyP, Helium, and Epigram</p>
<hr />
<div>Haskell, with its support for pattern matching on data structures,<br />
generic structure traversals, and expressive type system, is popular for<br />
implementing compilers and intepreters. Here's a selection of languages<br />
implemented in Haskell.<br />
<br />
=Large languages=<br />
<br />
==Haskell==<br />
<br />
;[http://haskell.org/ghc GHC]<br />
:GHC, The Glasgow Haskell Compiler, is written in Haskell<br />
<br />
;[[Yhc]]<br />
:Yhc, The York Haskell Compiler, is written in Haskell<br />
<br />
;[http://repetae.net/computer/jhc/ Jhc]<br />
:Jhc is a Haskell compiler which aims to produce the most efficient programs possible via whole program analysis<br />
<br />
;[http://haskell.org/nhc98 nhc98]<br />
:A compiler for Haskell 98, written in Haskell<br />
<br />
;[http://www.csg.lcs.mit.edu/projects/languages/ph.shtml pH]<br />
:A parallel version of Haskell from MIT.<br />
<br />
==Perl==<br />
<br />
;[http://pugscode.org Pugs]<br />
:Pugs is an implementation of Perl 6, written in Haskell. It aims to implement the full Perl6 specification.<br />
<br />
==Ruby==<br />
<br />
;[http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/7f3c1fd7d6c906aa RType]<br />
:RType is a Ruby interpreter written in Haskell<br />
<br />
==Scheme==<br />
<br />
;[http://halogen.note.amherst.edu/~jdtang/scheme_in_48/tutorial/overview.html Write Yourself a Scheme in 48 Hours]<br />
:A Haskell Tutorial.<br />
<br />
== Emacs Lisp ==<br />
;[http://www.codersbase.com/index.php/helisp Helisp]<br />
:The beginnings of an Emacs lisp compiler/interpreter.<br />
<br />
==Epigram==<br />
<br />
;[http://www.e-pig.org/ Epigram]<br />
:Epigram is a prototype dependently typed functional programming language<br />
<br />
==Curry==<br />
<br />
;[http://danae.uni-muenster.de/~lux/curry/ The M&uuml;nster Curry Compiler]<br />
:A native code compiler for the declarative multi-paradigm language Curry, written in Haskell<br />
<br />
==Bluespec==<br />
;[http://www.bluespec.com Bluespec]<br />
:A compiler for a hardware description language translating a Haskell-like (but with System Verilog syntax these days) language to Verilog.<br />
<br />
==Cayenne==<br />
;[http://www.depedent-types.org/ Cayenne]<br />
:A compiler for a Haskell-like language with depedent types.<br />
<br />
==Agda==<br />
;[http://www.cs.chalmers.se/~catarina/agda/ Agda]<br />
:A Cayenne-like programming language and proof assistant.<br />
<br />
==Chameleon==<br />
;[http://www.comp.nus.edu.sg/~sulzmann/chameleon/ Chameleon]<br />
:A Haskell-style language which implements the ideas described in a ``A Theory of Overloading``<br />
<br />
==Data Field Haskell==<br />
;[http://www.mrtc.mdh.se/projects/DFH/ Data Field Haskell]<br />
:A dialect of the functional programming language Haskell that provides an instance of data fields<br />
<br />
==Eden==<br />
;[http://www.mathematik.uni-marburg.de/~eden/ Eden]<br />
:A Haskell dialect for parallel programming<br />
<br />
==Generic Haskell==<br />
;[http://generic-haskell.org/ Generic Haskell]<br />
:An extension of Haskell that supports generic programming<br />
<br />
==PolyP==<br />
;[http://generic-haskell.org/ PolyP]<br />
:A polytypic programming language<br />
<br />
==Helium==<br />
;[http://www.cs.uu.nl/helium/ Helium]<br />
:A Haskell subset for educational purposes<br />
<br />
<br />
<br />
=Small languages=<br />
<br />
==Baskell==<br />
;[http://www.cs.mu.oz.au/~bjpop/code.html Baskell]<br />
:An interpreter for a small functional programming language. Supports strict and non-strict evaluation, and type inference. Useful for teaching purposes.<br />
<br />
==Unlambda==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/scripts/Unlambda.hs Unlambda.hs]<br />
:An implementation of unlambda in Haskell<br />
<br />
==BF==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/scripts/BF.hs BF.hs]<br />
:An implementation of BF in Haskell<br />
<br />
<br />
==Untyped lambda calculus==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/Plugin/Lambda/ LMEngine]<br />
:An implementation of the untyped lambda calculus<br />
<br />
==Epigram==<br />
<br />
;[http://www.e-pig.org/ Epigram]<br />
:A dependently typed programming language and an interactive programming environment<br />
<br />
<br />
<br />
{{LibrariesPage}}</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Applications_and_libraries/Compilers_and_interpreters&diff=7625Applications and libraries/Compilers and interpreters2006-10-30T19:35:15Z<p>HenkJanVanTuyl: </p>
<hr />
<div>Haskell, with its support for pattern matching on data structures,<br />
generic structure traversals, and expressive type system, is popular for<br />
implementing compilers and intepreters. Here's a selection of languages<br />
implemented in Haskell.<br />
<br />
=Large languages=<br />
<br />
==Haskell==<br />
<br />
;[http://haskell.org/ghc GHC]<br />
:GHC, The Glasgow Haskell Compiler, is written in Haskell<br />
<br />
;[[Yhc]]<br />
:Yhc, The York Haskell Compiler, is written in Haskell<br />
<br />
;[http://repetae.net/computer/jhc/ Jhc]<br />
:Jhc is a Haskell compiler which aims to produce the most efficient programs possible via whole program analysis<br />
<br />
;[http://haskell.org/nhc98 nhc98]<br />
:A compiler for Haskell 98, written in Haskell<br />
<br />
;[http://www.csg.lcs.mit.edu/projects/languages/ph.shtml pH]<br />
:A parallel version of Haskell from MIT.<br />
<br />
==Perl==<br />
<br />
;[http://pugscode.org Pugs]<br />
:Pugs is an implementation of Perl 6, written in Haskell. It aims to implement the full Perl6 specification.<br />
<br />
==Ruby==<br />
<br />
;[http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/7f3c1fd7d6c906aa RType]<br />
:RType is a Ruby interpreter written in Haskell<br />
<br />
==Scheme==<br />
<br />
;[http://halogen.note.amherst.edu/~jdtang/scheme_in_48/tutorial/overview.html Write Yourself a Scheme in 48 Hours]<br />
:A Haskell Tutorial.<br />
<br />
== Emacs Lisp ==<br />
;[http://www.codersbase.com/index.php/helisp Helisp]<br />
:The beginnings of an Emacs lisp compiler/interpreter.<br />
<br />
==Epigram==<br />
<br />
;[http://www.e-pig.org/ Epigram]<br />
:Epigram is a prototype dependently typed functional programming language<br />
<br />
==Curry==<br />
<br />
;[http://danae.uni-muenster.de/~lux/curry/ The M�nster Curry Compiler]<br />
:A native code compiler for the declarative multi-paradigm language Curry, written in Haskell<br />
<br />
==Bluespec==<br />
;[http://www.bluespec.com Bluespec]<br />
:A compiler for a hardware description language translating a Haskell-like (but with System Verilog syntax these days) language to Verilog.<br />
<br />
=Small languages=<br />
<br />
==Baskell==<br />
;[http://www.cs.mu.oz.au/~bjpop/code.html Baskell]<br />
:An interpreter for a small functional programming language. Supports strict and non-strict evaluation, and type inference. Useful for teaching purposes.<br />
<br />
==Unlambda==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/scripts/Unlambda.hs Unlambda.hs]<br />
:An implementation of unlambda in Haskell<br />
<br />
==BF==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/scripts/BF.hs BF.hs]<br />
:An implementation of BF in Haskell<br />
<br />
<br />
==Untyped lambda calculus==<br />
<br />
;[http://www.cse.unsw.edu.au/~dons/code/lambdabot/Plugin/Lambda/ LMEngine]<br />
:An implementation of the untyped lambda calculus<br />
<br />
{{LibrariesPage}}</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=HaWiki_migration&diff=6836HaWiki migration2006-10-09T20:12:10Z<p>HenkJanVanTuyl: </p>
<hr />
<div>Remember you need to '''check licences''' before moving content to the new wiki!<br />
Pages to be migrated to the new wiki. Once a page has been moved '''delete it from this list'''.<br />
<br />
When porting new pages, please try to categorise them. This will ease<br />
navigating the wiki in the long term. A list of all categories<br />
[[Special:Categories|is here]]. To categorise a page, insert, for example,<br />
the text:<br />
<haskell><br />
[[Category:Tutorials]]<br />
</haskell><br />
<br />
__TOC__<br />
<br />
== Licenses ==<br />
<br />
If you are happy for your contributions on the old wiki to be relicensed<br />
and moved to the new wiki, please add your name to this list, so that<br />
others may move your contributions without fear:<br />
<br />
* Andrew Bromage<br />
* Cale Gibbard<br />
* David Himmelstrup (Lemmih)<br />
* Don Stewart<br />
* Einar Karttunen<br />
* Henk-Jan van Tuyl<br />
* Henning Thielemann<br />
* Ian Lynagh<br />
* Jason Dagit<br />
* Matthias Fischmann<br />
* Neil Mitchell<br />
* Oleg Kiselyov<br />
* Shae Erisson<br />
* Stefan Aeschbacher<br />
<br />
Add yourself in `sort' order.<br />
<br />
== Uncategorised pages ==<br />
<br />
Please mark pages which you consider ''not'' to be moved which a *.<br />
<br />
===A to E ===<br />
<br />
[http://www.haskell.org/hawiki/ActorModel ActorModel]<br />
<br />
[http://www.haskell.org/hawiki/AdHocPolymorphism AdHocPolymorphism]<br />
<br />
[http://www.haskell.org/hawiki/AddingStrictness AddingStrictness]<br />
<br />
[http://www.haskell.org/hawiki/AlgorithmCorner AlgorithmCorner]<br />
<br />
[http://www.haskell.org/hawiki/AnonymousFunction AnonymousFunction]<br />
<br />
[http://www.haskell.org/hawiki/AntiBuddha AntiBuddha]<br />
<br />
[http://www.haskell.org/hawiki/ArraysInHaskell ArraysInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/ArraysVsTuples ArraysVsTuples]<br />
<br />
[http://www.haskell.org/hawiki/ArrowRun ArrowRun]<br />
<br />
[http://www.haskell.org/hawiki/AvoidingParameterPassing AvoidingParameterPassing]<br />
<br />
[http://www.haskell.org/hawiki/BFInterpreter BFInterpreter]<br />
<br />
[http://www.haskell.org/hawiki/BaseCasesAndIdentities BaseCasesAndIdentities]<br />
<br />
[http://www.haskell.org/hawiki/BeginnerChronicle BeginnerChronicle]<br />
<br />
[http://www.haskell.org/hawiki/C++FromHaskell C++FromHaskell]<br />
<br />
[http://www.haskell.org/hawiki/C2hs C2hs]<br />
<br />
[http://www.haskell.org/hawiki/CAF CAF]<br />
<br />
[http://www.haskell.org/hawiki/camelCase camelCase]<br />
<br />
[http://www.haskell.org/hawiki/Case Case]<br />
<br />
[http://www.haskell.org/hawiki/CatInHaskell CatInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/CategoryTheory/NaturalTransformation CategoryTheory/NaturalTransformation]<br />
<br />
[http://www.haskell.org/hawiki/CategoryTheory/Functor CategoryTheory/Functor]<br />
<br />
[http://www.haskell.org/hawiki/ChainOfResponsibility ChainOfResponsibility]<br />
<br />
[http://www.haskell.org/hawiki/CircularProgramming CircularProgramming]<br />
<br />
[http://www.haskell.org/hawiki/Closure Closure]<br />
<br />
[http://www.haskell.org/hawiki/CoMonad CoMonad]<br />
<br />
[http://www.haskell.org/hawiki/CoMonadArticles CoMonadArticles]<br />
<br />
[http://www.haskell.org/hawiki/CodeExamples CodeExamples]<br />
<br />
[http://www.haskell.org/hawiki/CodeOnTheWiki CodeOnTheWiki]<br />
<br />
[http://www.haskell.org/hawiki/CodeSnippets CodeSnippets]<br />
<br />
[http://www.haskell.org/hawiki/CombinatorPattern CombinatorPattern]<br />
<br />
[http://www.haskell.org/hawiki/CommonHaskellIdioms CommonHaskellIdioms]<br />
<br />
[http://www.haskell.org/hawiki/CommunityIssues CommunityIssues]<br />
<br />
[http://www.haskell.org/hawiki/Composition Composition]<br />
<br />
[http://www.haskell.org/hawiki/ConcreteTypeInstantiation ConcreteTypeInstantiation]<br />
<br />
[http://www.haskell.org/hawiki/ConcreteView ConcreteView]<br />
<br />
[http://www.haskell.org/hawiki/ConcurrencyWithOracles ConcurrencyWithOracles]<br />
<br />
[http://www.haskell.org/hawiki/ConcurrentTutorial ConcurrentTutorial]<br />
<br />
[http://www.haskell.org/hawiki/ConstantApplicativeForm ConstantApplicativeForm]<br />
<br />
[http://www.haskell.org/hawiki/ContinuationPassingStyle ContinuationPassingStyle]<br />
<br />
[http://www.haskell.org/hawiki/ContinuationsDoneRight ContinuationsDoneRight]<br />
<br />
[http://www.haskell.org/hawiki/ControlOperation ControlOperation]<br />
<br />
[http://www.haskell.org/hawiki/ConundrumBinaryOperationsOnInts ConundrumBinaryOperationsOnInts]<br />
<br />
[http://www.haskell.org/hawiki/ConvertingNumbers ConvertingNumbers]<br />
<br />
[http://www.haskell.org/hawiki/CpsInCee CpsInCee]<br />
<br />
[http://www.haskell.org/hawiki/CpsInJava CpsInJava]<br />
<br />
[http://www.haskell.org/hawiki/CpsTransform CpsTransform]<br />
<br />
[http://www.haskell.org/hawiki/CreditCardTransform CreditCardTransform]<br />
<br />
[http://www.haskell.org/hawiki/Currying Currying]<br />
<br />
[http://www.haskell.org/hawiki/DataStructureFreeTransformation DataStructureFreeTransformation]<br />
<br />
[http://www.haskell.org/hawiki/DataStructuresNotFunctions DataStructuresNotFunctions]<br />
<br />
[http://www.haskell.org/hawiki/DeFunctionalization DeFunctionalization]<br />
<br />
[http://www.haskell.org/hawiki/DebianUsers DebianUsers]<br />
<br />
[http://www.haskell.org/hawiki/DecoratingStructures DecoratingStructures]<br />
<br />
[http://www.haskell.org/hawiki/DerivingFromSpecifications DerivingFromSpecifications]<br />
<br />
[http://www.haskell.org/hawiki/DerivingInstancesFromTypes DerivingInstancesFromTypes]<br />
<br />
[http://www.haskell.org/hawiki/DesignPatternsForFunctionalStrategicProgramming DesignPatternsForFunctionalStrategicProgramming]<br />
<br />
[http://www.haskell.org/hawiki/DomainSpecificLanguages DomainSpecificLanguages]<br />
<br />
[http://www.haskell.org/hawiki/DynamicErrors DynamicErrors]<br />
<br />
[http://www.haskell.org/hawiki/DynamicLinkingInGhc DynamicLinkingInGhc]<br />
<br />
[http://www.haskell.org/hawiki/DynamicTypes DynamicTypes]<br />
<br />
[http://www.haskell.org/hawiki/EasyUnification EasyUnification]<br />
<br />
[http://www.haskell.org/hawiki/ErLang ErLang]<br />
<br />
[http://www.haskell.org/hawiki/ErrorMonad ErrorMonad]<br />
<br />
[http://www.haskell.org/hawiki/EuroHaskell EuroHaskell]<br />
<br />
[http://www.haskell.org/hawiki/EventStats EventStats]<br />
<br />
[http://www.haskell.org/hawiki/EventStats/HitCounts EventStats/HitCounts]<br />
<br />
[http://www.haskell.org/hawiki/EventStats/UserAgents EventStats/UserAgents]<br />
<br />
[http://www.haskell.org/hawiki/ExactRealArithmetic ExactRealArithmetic]<br />
<br />
[http://www.haskell.org/hawiki/ExceptionMonad ExceptionMonad]<br />
<br />
[http://www.haskell.org/hawiki/ExistentialType ExistentialType]<br />
<br />
[http://www.haskell.org/hawiki/ExistentialTypes ExistentialTypes]<br />
<br />
[http://www.haskell.org/hawiki/Existentially_20quantified_20record_20constructors Existentially quantified record constructors]<br />
<br />
[http://www.haskell.org/hawiki/ExperimentalFeatures ExperimentalFeatures]<br />
<br />
[http://www.haskell.org/hawiki/ExternalCommands ExternalCommands]<br />
<br />
=== F to J ===<br />
[http://www.haskell.org/hawiki/FactoryFunction FactoryFunction]<br />
<br />
[http://www.haskell.org/hawiki/FasterFloatingPointWithGhc FasterFloatingPointWithGhc]<br />
<br />
[http://www.haskell.org/hawiki/FeatherweightPerl6 FeatherweightPerl6]<br />
<br />
[http://www.haskell.org/hawiki/FedoraHaskell FedoraHaskell]<br />
<br />
[http://www.haskell.org/hawiki/FedoraRepository FedoraRepository]<br />
<br />
[http://www.haskell.org/hawiki/FfiCookbook FfiCookbook]<br />
<br />
[http://www.haskell.org/hawiki/FfiExample FfiExample]<br />
<br />
[http://www.haskell.org/hawiki/FfiTutorial FfiTutorial]<br />
<br />
[http://www.haskell.org/hawiki/FfiWithArrays FfiWithArrays]<br />
<br />
[http://www.haskell.org/hawiki/FindPage FindPage]<br />
<br />
[http://www.haskell.org/hawiki/FirstClassModules FirstClassModules]<br />
<br />
[http://www.haskell.org/hawiki/FirstSteps FirstSteps]<br />
<br />
[http://www.haskell.org/hawiki/FixedPointCombinator FixedPointCombinator]<br />
<br />
[http://www.haskell.org/hawiki/ForcingEagerEvaluation ForcingEagerEvaluation]<br />
<br />
[http://www.haskell.org/hawiki/ForeignFunctionInterface ForeignFunctionInterface]<br />
<br />
[http://www.haskell.org/hawiki/FptoolsWithDarcs FptoolsWithDarcs]<br />
<br />
[http://www.haskell.org/hawiki/FreeNode FreeNode]<br />
<br />
[http://www.haskell.org/hawiki/FrequentlyAskedQuestions FrequentlyAskedQuestions]<br />
<br />
[http://www.haskell.org/hawiki/FrontPage FrontPage]<br />
<br />
[http://www.haskell.org/hawiki/FunDeps FunDeps]<br />
<br />
[http://www.haskell.org/hawiki/Function Function]<br />
<br />
[http://www.haskell.org/hawiki/FunctionalDependencies FunctionalDependencies]<br />
<br />
[http://www.haskell.org/hawiki/FunctionalDispatch FunctionalDispatch]<br />
<br />
[http://www.haskell.org/hawiki/FunctionsNotDataStructures FunctionsNotDataStructures]<br />
<br />
[http://www.haskell.org/hawiki/FundamentalConcepts FundamentalConcepts]<br />
<br />
[http://www.haskell.org/hawiki/FutureOfHaskellDiscussionDiscussion FutureOfHaskellDiscussionDiscussion]<br />
<br />
[http://www.haskell.org/hawiki/GADT_20with_20record_20syntax GADT with record syntax]<br />
<br />
[http://www.haskell.org/hawiki/GHCPackageDesignFAQ GHCPackageDesignFAQ]<br />
<br />
[http://www.haskell.org/hawiki/GaleHaskeller GaleHaskeller]<br />
<br />
[http://www.haskell.org/hawiki/GeneralisingTypes GeneralisingTypes]<br />
<br />
[http://www.haskell.org/hawiki/GenericHaskellServer GenericHaskellServer]<br />
<br />
[http://www.haskell.org/hawiki/GennethsCrazyDatabaseConnector GennethsCrazyDatabaseConnector]<br />
<br />
[http://www.haskell.org/hawiki/GentleHaskellDiscuss GentleHaskellDiscuss]<br />
<br />
[http://www.haskell.org/hawiki/GesuchteSeiten GesuchteSeiten]<br />
<br />
[http://www.haskell.org/hawiki/GhcConcurrency GhcConcurrency]<br />
<br />
[http://www.haskell.org/hawiki/GhcPerformance GhcPerformance]<br />
<br />
[http://www.haskell.org/hawiki/GlobalMutableState GlobalMutableState]<br />
<br />
[http://www.haskell.org/hawiki/GoodLargeTeachingProgram GoodLargeTeachingProgram]<br />
<br />
[http://www.haskell.org/hawiki/GreenCard GreenCard]<br />
<br />
[http://www.haskell.org/hawiki/GrepInHaskell GrepInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/Gtk2Hs Gtk2Hs]<br />
<br />
[http://www.haskell.org/hawiki/GtkHs GtkHs]<br />
<br />
[http://www.haskell.org/hawiki/GunnarKedenburg GunnarKedenburg]<br />
<br />
[http://www.haskell.org/hawiki/HaCanon HaCanon]<br />
<br />
[http://www.haskell.org/hawiki/HaWiki HaWiki]<br />
<br />
[http://www.haskell.org/hawiki/HaWikiUsers HaWikiUsers]<br />
<br />
[http://www.haskell.org/hawiki/Hacanon Hacanon]<br />
<br />
[http://www.haskell.org/hawiki/Hackage Hackage]<br />
<br />
[http://www.haskell.org/hawiki/Hackage/TODO Hackage/TODO]<br />
<br />
[http://www.haskell.org/hawiki/HackageDB HackageDB]<br />
<br />
[http://www.haskell.org/hawiki/HashConsing HashConsing]<br />
<br />
[http://www.haskell.org/hawiki/Haskell Haskell]<br />
<br />
[http://www.haskell.org/hawiki/HaskellBookstore HaskellBookstore]<br />
<br />
[http://www.haskell.org/hawiki/HaskellBookstore/TalkAbout HaskellBookstore/TalkAbout]<br />
<br />
[http://www.haskell.org/hawiki/HaskellChannelPeople HaskellChannelPeople]<br />
<br />
[http://www.haskell.org/hawiki/HaskellCheatSheet HaskellCheatSheet]<br />
<br />
[http://www.haskell.org/hawiki/HaskellCommunities HaskellCommunities]<br />
<br />
[http://www.haskell.org/hawiki/HaskellDbTutorial HaskellDbTutorial]<br />
<br />
[http://www.haskell.org/hawiki/HaskellDbandHsp HaskellDbandHsp]<br />
<br />
[http://www.haskell.org/hawiki/HaskellDemo HaskellDemo]<br />
<br />
[http://www.haskell.org/hawiki/HaskellEditor HaskellEditor]<br />
<br />
[http://www.haskell.org/hawiki/HaskellForCompilers HaskellForCompilers]<br />
<br />
[http://www.haskell.org/hawiki/HaskellGoPeople HaskellGoPeople]<br />
<br />
[http://www.haskell.org/hawiki/HaskellIde HaskellIde]<br />
<br />
[http://www.haskell.org/hawiki/HaskellImplementations HaskellImplementations]<br />
<br />
[http://www.haskell.org/hawiki/HaskellInaNutshell HaskellInaNutshell]<br />
<br />
[http://www.haskell.org/hawiki/HaskellIrcChannel HaskellIrcChannel]<br />
<br />
[http://www.haskell.org/hawiki/HaskellIrcPastePage HaskellIrcPastePage]<br />
<br />
[http://www.haskell.org/hawiki/HaskellIrcQuotePage HaskellIrcQuotePage]<br />
<br />
[http://www.haskell.org/hawiki/HaskellLanguage HaskellLanguage]<br />
<br />
[http://www.haskell.org/hawiki/HaskellMailClient HaskellMailClient]<br />
<br />
[http://www.haskell.org/hawiki/HaskellMode HaskellMode]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie HaskellNewbie]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/AppendingToLists HaskellNewbie/AppendingToLists]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/BooleanTests HaskellNewbie/BooleanTests]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/CombiningMonads HaskellNewbie/CombiningMonads]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/DivisionOfIntegers HaskellNewbie/DivisionOfIntegers]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/DuplicateInstanceDeclaration HaskellNewbie/DuplicateInstanceDeclaration]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/ExternalPrograms HaskellNewbie/ExternalPrograms]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/FactorialsComingOutZero HaskellNewbie/FactorialsComingOutZero]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/FileOperations HaskellNewbie/FileOperations]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/FunctionsAndEquality HaskellNewbie/FunctionsAndEquality]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/HaskellAsAMacroLanguage HaskellNewbie/HaskellAsAMacroLanguage]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/HaskellFromJava HaskellNewbie/HaskellFromJava]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/HaskellInterpreterUsage HaskellNewbie/HaskellInterpreterUsage]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/ImplementingUnzipWithFoldr HaskellNewbie/ImplementingUnzipWithFoldr]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/InfiniteCartesianProduct HaskellNewbie/InfiniteCartesianProduct]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/LazyVsStrictEvaluation HaskellNewbie/LazyVsStrictEvaluation]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/LetAndWhere HaskellNewbie/LetAndWhere]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/ListComprehensions HaskellNewbie/ListComprehensions]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/LowerCase HaskellNewbie/LowerCase]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/ModuleSystemImplementation HaskellNewbie/ModuleSystemImplementation]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/MonadicIO HaskellNewbie/MonadicIO]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/NamespaceClashes HaskellNewbie/NamespaceClashes]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/NumericTypes HaskellNewbie/NumericTypes]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/ObjectOrientedVsFunctionalProgramming HaskellNewbie/ObjectOrientedVsFunctionalProgramming]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/PrettyPrintingRecords HaskellNewbie/PrettyPrintingRecords]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/PrintingFloatingPointValues HaskellNewbie/PrintingFloatingPointValues]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/ReadsAndShowsPrec HaskellNewbie/ReadsAndShowsPrec]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/StringParsing HaskellNewbie/StringParsing]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/TcpIpAndNetworking HaskellNewbie/TcpIpAndNetworking]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/TestingWithGetArgs HaskellNewbie/TestingWithGetArgs]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/TracingEvaluation HaskellNewbie/TracingEvaluation]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/TypeSignatureBasicQuestion HaskellNewbie/TypeSignatureBasicQuestion]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/UntypedSKCalculus HaskellNewbie/UntypedSKCalculus]<br />
<br />
[http://www.haskell.org/hawiki/HaskellNewbie/WorkingWithRandomNumbers HaskellNewbie/WorkingWithRandomNumbers]<br />
<br />
[http://www.haskell.org/hawiki/HaskellOhSix HaskellOhSix]<br />
<br />
[http://www.haskell.org/hawiki/HaskellOnMac HaskellOnMac]<br />
<br />
[http://www.haskell.org/hawiki/HaskellOpenGl HaskellOpenGl]<br />
<br />
[http://www.haskell.org/hawiki/HaskellProverbs HaskellProverbs]<br />
<br />
[http://www.haskell.org/hawiki/HaskellServerPages HaskellServerPages]<br />
<br />
[http://www.haskell.org/hawiki/HaskellServerProgramming HaskellServerProgramming]<br />
<br />
[http://www.haskell.org/hawiki/HaskellStreamIO HaskellStreamIO]<br />
<br />
[http://www.haskell.org/hawiki/HaskellStyle HaskellStyle]<br />
<br />
[http://www.haskell.org/hawiki/HaskellSupportFramework HaskellSupportFramework]<br />
<br />
[http://www.haskell.org/hawiki/HaskellToJavaByteCodeCompiler HaskellToJavaByteCodeCompiler]<br />
<br />
[http://www.haskell.org/hawiki/HaskellTutorials HaskellTutorials]<br />
<br />
[http://www.haskell.org/hawiki/HaskellTwo HaskellTwo]<br />
<br />
[http://www.haskell.org/hawiki/HaskellUnixDaemon HaskellUnixDaemon]<br />
<br />
[http://www.haskell.org/hawiki/HaskellUserLocations HaskellUserLocations]<br />
<br />
[http://www.haskell.org/hawiki/HaskellWebApplicationServer HaskellWebApplicationServer]<br />
<br />
[http://www.haskell.org/hawiki/HaskellWebApplications HaskellWebApplications]<br />
<br />
[http://www.haskell.org/hawiki/HaskellWikiInHaskell HaskellWikiInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/HaskellWishList HaskellWishList]<br />
<br />
[http://www.haskell.org/hawiki/HaskellWorldDomination HaskellWorldDomination]<br />
<br />
[http://www.haskell.org/hawiki/Haskore Haskore]<br />
<br />
[http://www.haskell.org/hawiki/HelloWorld HelloWorld]<br />
<br />
[http://www.haskell.org/hawiki/HelpContents HelpContents]<br />
<br />
[http://www.haskell.org/hawiki/HelpForBeginners HelpForBeginners]<br />
<br />
[http://www.haskell.org/hawiki/HelpForDevelopers HelpForDevelopers]<br />
<br />
[http://www.haskell.org/hawiki/HelpIndex HelpIndex]<br />
<br />
[http://www.haskell.org/hawiki/HelpMiscellaneous/FrequentlyAskedQuestions HelpMiscellaneous/FrequentlyAskedQuestions]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnAdministration HelpOnAdministration]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnConfiguration HelpOnConfiguration]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnConfiguration/EmailSupport HelpOnConfiguration/EmailSupport]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnConfiguration/SecurityPolicy HelpOnConfiguration/SecurityPolicy]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnEditing/SubPages HelpOnEditing/SubPages]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnFormatting HelpOnFormatting]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnHeadlines HelpOnHeadlines]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnLinking HelpOnLinking]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnLists HelpOnLists]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnMacros HelpOnMacros]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnNavigation HelpOnNavigation]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnPageCreation HelpOnPageCreation]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnPageDeletion HelpOnPageDeletion]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnProcessingInstructions HelpOnProcessingInstructions]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnProcessors HelpOnProcessors]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnRules HelpOnRules]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnSkins HelpOnSkins]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnSmileys HelpOnSmileys]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnSpellCheck HelpOnSpellCheck]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnTables HelpOnTables]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnUpdating HelpOnUpdating]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnUserPreferences HelpOnUserPreferences]<br />
<br />
[http://www.haskell.org/hawiki/HelpOnXmlPages HelpOnXmlPages]<br />
<br />
[http://www.haskell.org/hawiki/HelpTemplate HelpTemplate]<br />
<br />
[http://www.haskell.org/hawiki/hIDE hIDE]<br />
<br />
[http://www.haskell.org/hawiki/hIDE/Design hIDE/Design]<br />
<br />
[http://www.haskell.org/hawiki/hIDE/Win32 hIDE/Win32]<br />
<br />
[http://www.haskell.org/hawiki/HigherOrderFunction HigherOrderFunction]<br />
<br />
[http://www.haskell.org/hawiki/HigherOrderFunctions HigherOrderFunctions]<br />
<br />
[http://www.haskell.org/hawiki/HigherOrderFunctionsToCapture HigherOrderFunctionsToCapture]<br />
<br />
[http://www.haskell.org/hawiki/HilfeAllgemein HilfeAllgemein]<br />
<br />
[http://www.haskell.org/hawiki/HilfeAllgemein/FragenUndAntworten HilfeAllgemein/FragenUndAntworten]<br />
<br />
[http://www.haskell.org/hawiki/HilfeFürAnfänger HilfeFürAnfänger]<br />
<br />
[http://www.haskell.org/hawiki/HilfeFürEntwickler HilfeFürEntwickler]<br />
<br />
[http://www.haskell.org/hawiki/HilfeIndex HilfeIndex]<br />
<br />
[http://www.haskell.org/hawiki/HilfeInhalt HilfeInhalt]<br />
<br />
[http://www.haskell.org/hawiki/HilfeTemplate HilfeTemplate]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuAktionen HilfeZuAktionen]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuAktionen/DateiAnhänge HilfeZuAktionen/DateiAnhänge]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuBenutzerEinstellungen HilfeZuBenutzerEinstellungen]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuLayouts HilfeZuLayouts]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuListen HilfeZuListen]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuMakros HilfeZuMakros]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuSmileys HilfeZuSmileys]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuTabellen HilfeZuTabellen]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuTrennlinien HilfeZuTrennlinien]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuVerarbeitungsAnweisungen HilfeZuVerarbeitungsAnweisungen]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuVerweisen HilfeZuVerweisen]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuXmlSeiten HilfeZuXmlSeiten]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZumEditieren HilfeZumEditieren]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZumEditieren/UnterSeiten HilfeZumEditieren/UnterSeiten]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZumFormatieren HilfeZumFormatieren]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZumUpdaten HilfeZumUpdaten]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurAdministration HilfeZurAdministration]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurInstallation HilfeZurInstallation]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurInstallation/ApacheAufUnix HilfeZurInstallation/ApacheAufUnix]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurInstallation/ApacheAufWin32 HilfeZurInstallation/ApacheAufWin32]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurInstallation/BasisInstallation HilfeZurInstallation/BasisInstallation]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurInstallation/FehlerBehebung HilfeZurInstallation/FehlerBehebung]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurKonfiguration HilfeZurKonfiguration]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurNavigation HilfeZurNavigation]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurRechtschreibprüfung HilfeZurRechtschreibprüfung]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurSeitenErzeugung HilfeZurSeitenErzeugung]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZurSeitenLöschung HilfeZurSeitenLöschung]<br />
<br />
[http://www.haskell.org/hawiki/HilfeZuÜberschriften HilfeZuÜberschriften]<br />
<br />
[http://www.haskell.org/hawiki/HirculesIrcClient HirculesIrcClient]<br />
<br />
[http://www.haskell.org/hawiki/HomepageTemplate HomepageTemplate]<br />
<br />
[http://www.haskell.org/hawiki/HomeworkHelp HomeworkHelp]<br />
<br />
[http://www.haskell.org/hawiki/HowToReadHaskell HowToReadHaskell]<br />
<br />
[http://www.haskell.org/hawiki/HsWebForms HsWebForms]<br />
<br />
[http://www.haskell.org/hawiki/Hsc2hs Hsc2hs]<br />
<br />
[http://www.haskell.org/hawiki/HuGs HuGs]<br />
<br />
[http://www.haskell.org/hawiki/IcfpContest IcfpContest]<br />
<br />
[http://www.haskell.org/hawiki/IdentityMonad IdentityMonad]<br />
<br />
[http://www.haskell.org/hawiki/ImperativeHaskell ImperativeHaskell]<br />
<br />
[http://www.haskell.org/hawiki/ImplicitParameters ImplicitParameters]<br />
<br />
[http://www.haskell.org/hawiki/ImplicitReader ImplicitReader]<br />
<br />
[http://www.haskell.org/hawiki/IncreasingSharing IncreasingSharing]<br />
<br />
[http://www.haskell.org/hawiki/IndeterminismAndNondeterminism IndeterminismAndNondeterminism]<br />
<br />
[http://www.haskell.org/hawiki/IndirectComposite IndirectComposite]<br />
<br />
[http://www.haskell.org/hawiki/InfixExpression InfixExpression]<br />
<br />
[http://www.haskell.org/hawiki/InformationHiding InformationHiding]<br />
<br />
[http://www.haskell.org/hawiki/InstallationLogs InstallationLogs]<br />
<br />
[http://www.haskell.org/hawiki/InstallationTips InstallationTips]<br />
<br />
[http://www.haskell.org/hawiki/IntegralApproximation IntegralApproximation]<br />
<br />
[http://www.haskell.org/hawiki/IntegratingHaskellWikiInHaskellOrg IntegratingHaskellWikiInHaskellOrg]<br />
<br />
[http://www.haskell.org/hawiki/InterProcess InterProcess]<br />
<br />
[http://www.haskell.org/hawiki/InterWiki InterWiki]<br />
<br />
[http://www.haskell.org/hawiki/InteractiveFictionDecompilers InteractiveFictionDecompilers]<br />
<br />
[http://www.haskell.org/hawiki/IntermediateForm IntermediateForm]<br />
<br />
[http://www.haskell.org/hawiki/IntermediateRepresentation IntermediateRepresentation]<br />
<br />
[http://www.haskell.org/hawiki/IntroductionToIO IntroductionToIO]<br />
<br />
[http://www.haskell.org/hawiki/IterationPattern IterationPattern]<br />
<br />
[http://www.haskell.org/hawiki/Jhc Jhc]<br />
<br />
[http://www.haskell.org/hawiki/JournalOfFunctionalProgramming JournalOfFunctionalProgramming]<br />
<br />
=== K to O ===<br />
[http://www.haskell.org/hawiki/Keywords Keywords]<br />
<br />
[http://www.haskell.org/hawiki/KnuthMorrisPratt KnuthMorrisPratt]<br />
<br />
[http://www.haskell.org/hawiki/LambdaDropping LambdaDropping]<br />
<br />
[http://www.haskell.org/hawiki/LearningHaskell LearningHaskell]<br />
<br />
[http://www.haskell.org/hawiki/LessFrequentlyAskedQuestions LessFrequentlyAskedQuestions]<br />
<br />
[http://www.haskell.org/hawiki/LetFloating LetFloating]<br />
<br />
[http://www.haskell.org/hawiki/LibrariesAndTools LibrariesAndTools]<br />
<br />
[http://www.haskell.org/hawiki/LibrariesExts LibrariesExts]<br />
<br />
[http://www.haskell.org/hawiki/LibrariesExts/QuickCheckExts LibrariesExts/QuickCheckExts]<br />
<br />
[http://www.haskell.org/hawiki/LibrariesTest LibrariesTest]<br />
<br />
[http://www.haskell.org/hawiki/LibraryDocsNeedingHelp LibraryDocsNeedingHelp]<br />
<br />
[http://www.haskell.org/hawiki/LibraryDocumentation LibraryDocumentation]<br />
<br />
[http://www.haskell.org/hawiki/LibraryDocumentation/CPUTime LibraryDocumentation/CPUTime]<br />
<br />
[http://www.haskell.org/hawiki/LibraryDocumentation/Ix LibraryDocumentation/Ix]<br />
<br />
[http://www.haskell.org/hawiki/LibraryDocumentation/Prelude LibraryDocumentation/Prelude]<br />
<br />
[http://www.haskell.org/hawiki/LibraryInfrastructure LibraryInfrastructure]<br />
<br />
[http://www.haskell.org/hawiki/LibraryInfrastructure/Comments LibraryInfrastructure/Comments]<br />
<br />
[http://www.haskell.org/hawiki/LibraryInfrastructure/MetaData LibraryInfrastructure/MetaData]<br />
<br />
[http://www.haskell.org/hawiki/LibraryInfrastructure/RelatedSystems LibraryInfrastructure/RelatedSystems]<br />
<br />
[http://www.haskell.org/hawiki/LibraryInfrastructureNotes LibraryInfrastructureNotes]<br />
<br />
[http://www.haskell.org/hawiki/LicensedPreludeExts LicensedPreludeExts]<br />
<br />
[http://www.haskell.org/hawiki/Lifting Lifting]<br />
<br />
[http://www.haskell.org/hawiki/LiftingPattern LiftingPattern]<br />
<br />
[http://www.haskell.org/hawiki/LinearAlgebra LinearAlgebra]<br />
<br />
[http://www.haskell.org/hawiki/ListMutation ListMutation]<br />
<br />
[http://www.haskell.org/hawiki/ListTDoneRight ListTDoneRight]<br />
<br />
[http://www.haskell.org/hawiki/ListTDoneRight/Alternative1 ListTDoneRight/Alternative1]<br />
<br />
[http://www.haskell.org/hawiki/LiyangHu LiyangHu]<br />
<br />
[http://www.haskell.org/hawiki/LocalBadContent LocalBadContent]<br />
<br />
[http://www.haskell.org/hawiki/LocalSpellingWords LocalSpellingWords]<br />
<br />
[http://www.haskell.org/hawiki/Mandelbrot Mandelbrot]<br />
<br />
[http://www.haskell.org/hawiki/MaybeMonad MaybeMonad]<br />
<br />
[http://www.haskell.org/hawiki/MegaMonad MegaMonad]<br />
<br />
[http://www.haskell.org/hawiki/MegaMonad/PassingTuringTest MegaMonad/PassingTuringTest]<br />
<br />
[http://www.haskell.org/hawiki/MemoisingCafs MemoisingCafs]<br />
<br />
[http://www.haskell.org/hawiki/MemoizingRecursion MemoizingRecursion]<br />
<br />
[http://www.haskell.org/hawiki/MergingSTThreads MergingSTThreads]<br />
<br />
[http://www.haskell.org/hawiki/MessageDispatching MessageDispatching]<br />
<br />
[http://www.haskell.org/hawiki/ModelizingInHaskell ModelizingInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/Monad Monad]<br />
<br />
[http://www.haskell.org/hawiki/MonadCont MonadCont]<br />
<br />
[http://www.haskell.org/hawiki/MonadError MonadError]<br />
<br />
[http://www.haskell.org/hawiki/MonadIdentity MonadIdentity]<br />
<br />
[http://www.haskell.org/hawiki/MonadPlus MonadPlus]<br />
<br />
[http://www.haskell.org/hawiki/MonadReader MonadReader]<br />
<br />
[http://www.haskell.org/hawiki/MonadRun MonadRun]<br />
<br />
[http://www.haskell.org/hawiki/MonadState MonadState]<br />
<br />
[http://www.haskell.org/hawiki/MonadStream MonadStream]<br />
<br />
[http://www.haskell.org/hawiki/MonadTemplateLibrary MonadTemplateLibrary]<br />
<br />
[http://www.haskell.org/hawiki/MonadTemplateLibrary/MonadRandom MonadTemplateLibrary/MonadRandom]<br />
<br />
[http://www.haskell.org/hawiki/MonadTransformer MonadTransformer]<br />
<br />
[http://www.haskell.org/hawiki/MonadTransformerPuzzle MonadTransformerPuzzle]<br />
<br />
[http://www.haskell.org/hawiki/MonadTransformers MonadTransformers]<br />
<br />
[http://www.haskell.org/hawiki/MonadUnTrans MonadUnTrans]<br />
<br />
[http://www.haskell.org/hawiki/MonadWriter MonadWriter]<br />
<br />
[http://www.haskell.org/hawiki/MonadicContinuationPassingStyle MonadicContinuationPassingStyle]<br />
<br />
[http://www.haskell.org/hawiki/MonadicStyleTransformation MonadicStyleTransformation]<br />
<br />
[http://www.haskell.org/hawiki/MonomorphismRestriction MonomorphismRestriction]<br />
<br />
[http://www.haskell.org/hawiki/NewsAndDiscussions NewsAndDiscussions]<br />
<br />
[http://www.haskell.org/hawiki/NewtypeVersusStrictData NewtypeVersusStrictData]<br />
<br />
[http://www.haskell.org/hawiki/NickMain NickMain]<br />
<br />
[http://www.haskell.org/hawiki/NoPatternGuards NoPatternGuards]<br />
<br />
[http://www.haskell.org/hawiki/NonDeterminism NonDeterminism]<br />
<br />
[http://www.haskell.org/hawiki/NonstrictFromStrict NonstrictFromStrict]<br />
<br />
[http://www.haskell.org/hawiki/NotJustMaybe NotJustMaybe]<br />
<br />
[http://www.haskell.org/hawiki/NullObject NullObject]<br />
<br />
[http://www.haskell.org/hawiki/ObjectIoLibrary ObjectIoLibrary]<br />
<br />
[http://www.haskell.org/hawiki/OpenQuestions OpenQuestions]<br />
<br />
=== P to T ===<br />
[http://www.haskell.org/hawiki/PacketClass PacketClass]<br />
<br />
[http://www.haskell.org/hawiki/PairsOfIdentifiers PairsOfIdentifiers]<br />
<br />
[http://www.haskell.org/hawiki/ParameterizedDataType ParameterizedDataType]<br />
<br />
[http://www.haskell.org/hawiki/ParsingForeignLanguagesInHaskell ParsingForeignLanguagesInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/ParsingInHaskell ParsingInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/PartialApplication PartialApplication]<br />
<br />
[http://www.haskell.org/hawiki/PartialEvaluation PartialEvaluation]<br />
<br />
[http://www.haskell.org/hawiki/PartialEvaluationToCaptureSeparationOfConcerns PartialEvaluationToCaptureSeparationOfConcerns]<br />
<br />
[http://www.haskell.org/hawiki/PatternGuards PatternGuards]<br />
<br />
[http://www.haskell.org/hawiki/PatternMatching PatternMatching]<br />
<br />
[http://www.haskell.org/hawiki/PaulGrahamAccumulatorProblem PaulGrahamAccumulatorProblem]<br />
<br />
[http://www.haskell.org/hawiki/Perl6UsersGolfingSystem Perl6UsersGolfingSystem]<br />
<br />
[http://www.haskell.org/hawiki/PerlUsersGolfingSystem PerlUsersGolfingSystem]<br />
<br />
[http://www.haskell.org/hawiki/PermutationExample PermutationExample]<br />
<br />
[http://www.haskell.org/hawiki/PhoneNumber PhoneNumber] (Lots of code by Paulohnson - need license check)<br />
<br />
[http://www.haskell.org/hawiki/PhysicalUnits PhysicalUnits]<br />
<br />
[http://www.haskell.org/hawiki/PipeliningFunctions PipeliningFunctions]<br />
<br />
[http://www.haskell.org/hawiki/Polymorphism Polymorphism]<br />
<br />
[http://www.haskell.org/hawiki/PracticalHaskell PracticalHaskell]<br />
<br />
[http://www.haskell.org/hawiki/PreludeExts PreludeExts]<br />
<br />
[http://www.haskell.org/hawiki/PreludeExtsUseExamples PreludeExtsUseExamples]<br />
<br />
[http://www.haskell.org/hawiki/PreludeListFunctions PreludeListFunctions]<br />
<br />
[http://www.haskell.org/hawiki/Profiling Profiling]<br />
<br />
[http://www.haskell.org/hawiki/ProjectIdeas ProjectIdeas]<br />
<br />
[http://www.haskell.org/hawiki/Proposed_20extensions Proposed extensions]<br />
<br />
[http://www.haskell.org/hawiki/PurityAndMonads PurityAndMonads]<br />
<br />
[http://www.haskell.org/hawiki/Python4Suite Python4Suite]<br />
<br />
[http://www.haskell.org/hawiki/PythonGenerator PythonGenerator]<br />
<br />
[http://www.haskell.org/hawiki/QuickCheck QuickCheck]<br />
<br />
[http://www.haskell.org/hawiki/QuotesPage QuotesPage]<br />
<br />
[http://www.haskell.org/hawiki/QuotesPage/DuelsPage QuotesPage/DuelsPage]<br />
<br />
[http://www.haskell.org/hawiki/RankTwoPolymorphism RankTwoPolymorphism]<br />
<br />
[http://www.haskell.org/hawiki/Recursion Recursion]<br />
<br />
[http://www.haskell.org/hawiki/RedEx RedEx]<br />
<br />
[http://www.haskell.org/hawiki/ReducibleExpression ReducibleExpression]<br />
<br />
[http://www.haskell.org/hawiki/ReferentialTransparency ReferentialTransparency]<br />
<br />
[http://www.haskell.org/hawiki/RegexSyntax RegexSyntax]<br />
<br />
[http://www.haskell.org/hawiki/RemoveCorrect RemoveCorrect]<br />
<br />
[http://www.haskell.org/hawiki/RpmsProject RpmsProject]<br />
<br />
[http://www.haskell.org/hawiki/RunTimeCompilation RunTimeCompilation]<br />
<br />
[http://www.haskell.org/hawiki/RuntimeModels RuntimeModels]<br />
<br />
[http://www.haskell.org/hawiki/RuntimeModuleLoading RuntimeModuleLoading]<br />
<br />
[http://www.haskell.org/hawiki/SandBox SandBox]<br />
<br />
[http://www.haskell.org/hawiki/ScanningInHaskell ScanningInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/SchwartzianTransform SchwartzianTransform]<br />
<br />
[http://www.haskell.org/hawiki/SeiteFinden SeiteFinden]<br />
<br />
[http://www.haskell.org/hawiki/SeitenGröße SeitenGröße]<br />
<br />
[http://www.haskell.org/hawiki/ShortExamples ShortExamples]<br />
<br />
[http://www.haskell.org/hawiki/ShortExamples/BFInterpreter ShortExamples/BFInterpreter]<br />
<br />
[http://www.haskell.org/hawiki/ShortExamples/Copalindromes ShortExamples/Copalindromes]<br />
<br />
[http://www.haskell.org/hawiki/ShortExamples/POPCleaner ShortExamples/POPCleaner]<br />
<br />
[http://www.haskell.org/hawiki/ShortExamples/SymbolDifferentiation ShortExamples/SymbolDifferentiation]<br />
<br />
[http://www.haskell.org/hawiki/SieveOfEratosthenes SieveOfEratosthenes]<br />
<br />
[http://www.haskell.org/hawiki/SillySignatures SillySignatures]<br />
<br />
[http://www.haskell.org/hawiki/SimulatedCaseSplitting SimulatedCaseSplitting]<br />
<br />
[http://www.haskell.org/hawiki/SimulatingDependentTypes SimulatingDependentTypes]<br />
<br />
[http://www.haskell.org/hawiki/SiteNavigation SiteNavigation]<br />
<br />
[http://www.haskell.org/hawiki/SocraticMethod SocraticMethod]<br />
<br />
[http://www.haskell.org/hawiki/SoftwareEvolution SoftwareEvolution]<br />
<br />
[http://www.haskell.org/hawiki/SpecialistDataStructures SpecialistDataStructures]<br />
<br />
[http://www.haskell.org/hawiki/SpreadSheet SpreadSheet]<br />
<br />
[http://www.haskell.org/hawiki/StackOverflow StackOverflow]<br />
<br />
[http://www.haskell.org/hawiki/StandardCollectionLibraries StandardCollectionLibraries]<br />
<br />
[http://www.haskell.org/hawiki/StandardCollectionLibraries/TODO StandardCollectionLibraries/TODO]<br />
<br />
[http://www.haskell.org/hawiki/StartSeite StartSeite]<br />
<br />
[http://www.haskell.org/hawiki/StateMonad StateMonad]<br />
<br />
[http://www.haskell.org/hawiki/StatePropagation StatePropagation]<br />
<br />
[http://www.haskell.org/hawiki/StdGen StdGen]<br />
<br />
[http://www.haskell.org/hawiki/StraFunski StraFunski]<br />
<br />
[http://www.haskell.org/hawiki/StructuredText StructuredText]<br />
<br />
[http://www.haskell.org/hawiki/StudlyCaps StudlyCaps]<br />
<br />
[http://www.haskell.org/hawiki/StudyGroup StudyGroup]<br />
<br />
[http://www.haskell.org/hawiki/StudyGroup/GraphExamplesInHaskell StudyGroup/GraphExamplesInHaskell]<br />
<br />
[http://www.haskell.org/hawiki/StudyGroup/GraphExamplesInHaskell/WhySum3 StudyGroup/GraphExamplesInHaskell/WhySum3]<br />
<br />
[http://www.haskell.org/hawiki/StudyGroup/Talk StudyGroup/Talk]<br />
<br />
[http://www.haskell.org/hawiki/SudokuSolver SudokuSolver]<br />
<br />
[http://www.haskell.org/hawiki/SvenMoritzHallberg SvenMoritzHallberg]<br />
<br />
[http://www.haskell.org/hawiki/SyntacticSugar SyntacticSugar]<br />
<br />
[http://www.haskell.org/hawiki/SyntacticSugar/Cons SyntacticSugar/Cons]<br />
<br />
[http://www.haskell.org/hawiki/SyntacticSugar/Pros SyntacticSugar/Pros]<br />
<br />
[http://www.haskell.org/hawiki/SystemInfo SystemInfo]<br />
<br />
[http://www.haskell.org/hawiki/splitAt splitAt]<br />
<br />
[http://www.haskell.org/hawiki/TailRecursive TailRecursive]<br />
<br />
[http://www.haskell.org/hawiki/TeamSuccGMT TeamSuccGMT]<br />
<br />
[http://www.haskell.org/hawiki/TemplateHaskell TemplateHaskell]<br />
<br />
[http://www.haskell.org/hawiki/TemplateHaskellTutorial TemplateHaskellTutorial]<br />
<br />
[http://www.haskell.org/hawiki/TextCtrl TextCtrl]<br />
<br />
[http://www.haskell.org/hawiki/ThatAnnoyingIOType ThatAnnoyingIOType]<br />
<br />
[http://www.haskell.org/hawiki/ThatAnnoyingIoType ThatAnnoyingIoType]<br />
<br />
[http://www.haskell.org/hawiki/TheGallery TheGallery]<br />
<br />
[http://www.haskell.org/hawiki/TheZipper TheZipper]<br />
<br />
[http://www.haskell.org/hawiki/TheZipper/Zipper TheZipper/Zipper]<br />
<br />
[http://www.haskell.org/hawiki/TheZipper/ZipperG TheZipper/ZipperG]<br />
<br />
[http://www.haskell.org/hawiki/ThingsToAvoid ThingsToAvoid]<br />
<br />
[http://www.haskell.org/hawiki/ThingsToAvoid/Discussion ThingsToAvoid/Discussion]<br />
<br />
[http://www.haskell.org/hawiki/TimeLibrary TimeLibrary]<br />
<br />
[http://www.haskell.org/hawiki/TipsAndTricks TipsAndTricks]<br />
<br />
[http://www.haskell.org/hawiki/TitleIndex TitleIndex]<br />
<br />
[http://www.haskell.org/hawiki/TrexStyleRecords TrexStyleRecords]<br />
<br />
[http://www.haskell.org/hawiki/TyingTheKnot TyingTheKnot]<br />
<br />
[http://www.haskell.org/hawiki/TypeClass TypeClass]<br />
<br />
[http://www.haskell.org/hawiki/TypeClassConstraint TypeClassConstraint]<br />
<br />
[http://www.haskell.org/hawiki/TypeInference TypeInference]<br />
<br />
[http://www.haskell.org/hawiki/TypeclassWrapper TypeclassWrapper]<br />
<br />
[http://www.haskell.org/hawiki/TypesOnShellLevel TypesOnShellLevel]<br />
<br />
=== U to Z===<br />
[http://www.haskell.org/hawiki/UnZip UnZip]<br />
<br />
[http://www.haskell.org/hawiki/UndecidableInstances UndecidableInstances]<br />
<br />
[http://www.haskell.org/hawiki/UndecidableProblem UndecidableProblem]<br />
<br />
[http://www.haskell.org/hawiki/UnderConstruction UnderConstruction]<br />
<br />
[http://www.haskell.org/hawiki/UnderestimatedTypeClasses UnderestimatedTypeClasses]<br />
<br />
[http://www.haskell.org/hawiki/UnderstandingArrows UnderstandingArrows]<br />
<br />
[http://www.haskell.org/hawiki/UnicodeHaskellSource UnicodeHaskellSource]<br />
<br />
[http://www.haskell.org/hawiki/UnicodeInputOutput UnicodeInputOutput]<br />
<br />
[http://www.haskell.org/hawiki/UnicodeIssues UnicodeIssues]<br />
<br />
[http://www.haskell.org/hawiki/UnicodePrelude UnicodePrelude]<br />
<br />
[http://www.haskell.org/hawiki/UnitObject UnitObject]<br />
<br />
[http://www.haskell.org/hawiki/UnitTesting UnitTesting]<br />
<br />
[http://www.haskell.org/hawiki/UpdateInPlace UpdateInPlace]<br />
<br />
[http://www.haskell.org/hawiki/UserPreferences UserPreferences]<br />
<br />
[http://www.haskell.org/hawiki/UsingHaskellWikiMaterial UsingHaskellWikiMaterial]<br />
<br />
[http://www.haskell.org/hawiki/UsingIo UsingIo]<br />
<br />
[http://www.haskell.org/hawiki/UsingLibraries UsingLibraries]<br />
<br />
[http://www.haskell.org/hawiki/UsingMonadTransformers UsingMonadTransformers]<br />
<br />
[http://www.haskell.org/hawiki/UsingMonads UsingMonads]<br />
<br />
[http://www.haskell.org/hawiki/UsingQualifiedNames UsingQualifiedNames]<br />
<br />
[http://www.haskell.org/hawiki/UsingRecords UsingRecords]<br />
<br />
[http://www.haskell.org/hawiki/Variable Variable]<br />
<br />
[http://www.haskell.org/hawiki/VersusQuotesPage VersusQuotesPage]<br />
<br />
[http://www.haskell.org/hawiki/VerwaisteSeiten VerwaisteSeiten]<br />
<br />
[http://www.haskell.org/hawiki/Views Views]<br />
<br />
[http://www.haskell.org/hawiki/Vim Vim]<br />
<br />
[http://www.haskell.org/hawiki/WaSh WaSh]<br />
<br />
[http://www.haskell.org/hawiki/WayOfTheFuture WayOfTheFuture]<br />
<br />
[http://www.haskell.org/hawiki/WebAppsFramework WebAppsFramework]<br />
<br />
[http://www.haskell.org/hawiki/Weroom Weroom]<br />
<br />
[http://www.haskell.org/hawiki/WhatDoesThatSymbolMean WhatDoesThatSymbolMean]<br />
<br />
[http://www.haskell.org/hawiki/WhatIsThePrelude WhatIsThePrelude]<br />
<br />
[http://www.haskell.org/hawiki/WhatIsaFold WhatIsaFold]<br />
<br />
[http://www.haskell.org/hawiki/WhileLoop WhileLoop]<br />
<br />
[http://www.haskell.org/hawiki/WhyFunctionalProgrammingMatters WhyFunctionalProgrammingMatters]<br />
<br />
[http://www.haskell.org/hawiki/WhyWikiWorks WhyWikiWorks]<br />
<br />
[http://www.haskell.org/hawiki/WikiHomePage WikiHomePage]<br />
<br />
[http://www.haskell.org/hawiki/WikiName WikiName]<br />
<br />
[http://www.haskell.org/hawiki/WikiSandBox WikiSandBox]<br />
<br />
[http://www.haskell.org/hawiki/WikiSyntax WikiSyntax]<br />
<br />
[http://www.haskell.org/hawiki/WikiUsers WikiUsers]<br />
<br />
[http://www.haskell.org/hawiki/WikiWiki WikiWiki]<br />
<br />
[http://www.haskell.org/hawiki/WikiWikiSandbox WikiWikiSandbox]<br />
<br />
[http://www.haskell.org/hawiki/WikiWikiWeb WikiWikiWeb]<br />
<br />
[http://www.haskell.org/hawiki/WinHugs WinHugs]<br />
<br />
[http://www.haskell.org/hawiki/WishList WishList]<br />
<br />
[http://www.haskell.org/hawiki/WordIndex WordIndex]<br />
<br />
[http://www.haskell.org/hawiki/WortIndex WortIndex]<br />
<br />
[http://www.haskell.org/hawiki/WritingCryptDiscordian WritingCryptDiscordian]<br />
<br />
[http://www.haskell.org/hawiki/XsltVersion XsltVersion]<br />
<br />
[http://www.haskell.org/hawiki/YetAnotherHaskellTutorial YetAnotherHaskellTutorial]<br />
<br />
[http://www.haskell.org/hawiki/YetAnotherStudyGuide YetAnotherStudyGuide]<br />
<br />
<br />
<br />
== Monad Reader ==<br />
<br />
Shapr? Want to move these?<br />
<br />
[http://www.haskell.org/hawiki/TheMonadReader TheMonadReader]<br />
<br />
[http://www.haskell.org/hawiki/TheMonadReader/IssueOne TheMonadReader/IssueOne]<br />
<br />
[http://www.haskell.org/hawiki/TheMonadReader/IssueOne/FeedBack TheMonadReader/IssueOne/FeedBack]<br />
<br />
[http://www.haskell.org/hawiki/TheMonadReader/IssueOne/FeedBack/Gtk2Hs TheMonadReader/IssueOne/FeedBack/Gtk2Hs]<br />
<br />
[http://www.haskell.org/hawiki/TheMonadReader/IssueOne/FeedBack/PseudoCode TheMonadReader/IssueOne/FeedBack/PseudoCode]<br />
<br />
[http://www.haskell.org/hawiki/TheMonadReader/IssueOne/FeedBack/PugsProject TheMonadReader/IssueOne/FeedBack/PugsProject]<br />
<br />
== Cabal pages ==<br />
<br />
[http://www.haskell.org/hawiki/Cabal Cabal]<br />
<br />
[http://www.haskell.org/hawiki/Cabal/AggregatePackages Cabal/AggregatePackages]<br />
<br />
[http://www.haskell.org/hawiki/Cabal/Bugs Cabal/Bugs]<br />
<br />
[http://www.haskell.org/hawiki/Cabal/ConditionalCodeAndDependencies Cabal/ConditionalCodeAndDependencies]<br />
<br />
[http://www.haskell.org/hawiki/Cabal/Design Cabal/Design]<br />
<br />
[http://www.haskell.org/hawiki/Cabal/MultiPackageDistributables Cabal/MultiPackageDistributables]<br />
<br />
[http://www.haskell.org/hawiki/Cabal/TODO Cabal/TODO]<br />
<br />
[http://www.haskell.org/hawiki/Cabal/VirtualizationRequirements Cabal/VirtualizationRequirements]<br />
<br />
[http://www.haskell.org/hawiki/CabalGet CabalGet]<br />
<br />
== Pages in German ==<br />
<br />
[http://www.haskell.org/hawiki/AktuelleÄnderungen AktuelleÄnderungen]<br />
<br />
[http://www.haskell.org/hawiki/AufgegebeneSeiten AufgegebeneSeiten]<br />
<br />
[http://www.haskell.org/hawiki/BenutzerEinstellungen BenutzerEinstellungen]<br />
<br />
== Hawiki Infrastructure pages ==<br />
<br />
These pages can just disappear too.<br />
<br />
[http://www.haskell.org/hawiki/CategoryCodeSnippet CategoryCodeSnippet]<br />
<br />
[http://www.haskell.org/hawiki/CategoryCommunity CategoryCommunity]<br />
<br />
[http://www.haskell.org/hawiki/CategoryHaskellImplementations CategoryHaskellImplementations]<br />
<br />
[http://www.haskell.org/hawiki/CategoryHomepage CategoryHomepage]<br />
<br />
[http://www.haskell.org/hawiki/CategoryMonad CategoryMonad]<br />
<br />
[http://www.haskell.org/hawiki/CategoryCategory CategoryCategory]<br />
<br />
[http://www.haskell.org/hawiki/CategoryTools CategoryTools]<br />
<br />
[http://www.haskell.org/hawiki/CategoryTutorial CategoryTutorial]<br />
<br />
[http://www.haskell.org/hawiki/CategoryApplication CategoryApplication]<br />
<br />
[http://www.haskell.org/hawiki/AdminGroup AdminGroup]<br />
<br />
[http://www.haskell.org/hawiki/BadContent BadContent]<br />
<br />
[http://www.haskell.org/hawiki/WhyWikiWorks WhyWikiWorks]<br />
<br />
[http://www.haskell.org/hawiki/WikiHomePage WikiHomePage]<br />
<br />
[http://www.haskell.org/hawiki/WikiName WikiName]<br />
<br />
[http://www.haskell.org/hawiki/WikiSandBox WikiSandBox]<br />
<br />
[http://www.haskell.org/hawiki/WikiSyntax WikiSyntax]<br />
<br />
[http://www.haskell.org/hawiki/WikiUsers WikiUsers]<br />
<br />
[http://www.haskell.org/hawiki/WikiWiki WikiWiki]<br />
<br />
[http://www.haskell.org/hawiki/WikiWikiSandbox WikiWikiSandbox]<br />
<br />
[http://www.haskell.org/hawiki/WikiWikiWeb WikiWikiWeb]<br />
<br />
== User pages ==<br />
<br />
These pages may just disappear. Users can recreate them on the new wiki<br />
<br />
[http://www.haskell.org/hawiki/AaronDenney AaronDenney] [http://www.haskell.org/hawiki/AbeEgnor AbeEgnor] [http://www.haskell.org/hawiki/AlastairReid AlastairReid] [http://www.haskell.org/hawiki/AlbertReiner AlbertReiner] [http://www.haskell.org/hawiki/AlexanderWilliams AlexanderWilliams] [http://www.haskell.org/hawiki/AllenWoolfrey AllenWoolfrey] [http://www.haskell.org/hawiki/AlsonKemp AlsonKemp] [http://www.haskell.org/hawiki/AndersCarlsson AndersCarlsson] [http://www.haskell.org/hawiki/AndersHöckersten AndersHöckersten] [http://www.haskell.org/hawiki/AndrePang AndrePang] [http://www.haskell.org/hawiki/AndrewBromage AndrewBromage] [http://www.haskell.org/hawiki/AndreySidorenko AndreySidorenko] [http://www.haskell.org/hawiki/AndréPang AndréPang] [http://www.haskell.org/hawiki/AndyGeorges AndyGeorges] [http://www.haskell.org/hawiki/AndyGill AndyGill] [http://www.haskell.org/hawiki/AntonioRegidorGarcia AntonioRegidorGarcia] [http://www.haskell.org/hawiki/AnttiJuhaniKaijanaho AnttiJuhaniKaijanaho] [http://www.haskell.org/hawiki/AudreyTang AudreyTang] [http://www.haskell.org/hawiki/AutrijusTang AutrijusTang] [http://www.haskell.org/hawiki/BastiaanZapf BastiaanZapf] [http://www.haskell.org/hawiki/BernardPope BernardPope] [http://www.haskell.org/hawiki/BjarkeDahlEbert BjarkeDahlEbert] [http://www.haskell.org/hawiki/BjornBringert BjornBringert] [http://www.haskell.org/hawiki/BorislavManolov BorislavManolov] [http://www.haskell.org/hawiki/BrandonMoore BrandonMoore] [http://www.haskell.org/hawiki/BrianEdwards BrianEdwards] [http://www.haskell.org/hawiki/BruceWilliams BruceWilliams] [http://www.haskell.org/hawiki/BryanMurphy BryanMurphy] [http://www.haskell.org/hawiki/BrynKeller BrynKeller] [http://www.haskell.org/hawiki/BulatZiganshin BulatZiganshin] [http://www.haskell.org/hawiki/CaleGibbard CaleGibbard] [http://www.haskell.org/hawiki/CaleGibbard/BSDLicense CaleGibbard/BSDLicense] [http://www.haskell.org/hawiki/ChrisAngus ChrisAngus] [http://www.haskell.org/hawiki/ChrisMilton ChrisMilton] [http://www.haskell.org/hawiki/ChristophePoucet ChristophePoucet] [http://www.haskell.org/hawiki/ChristopherHendrie ChristopherHendrie] [http://www.haskell.org/hawiki/ColinDeVilbiss ColinDeVilbiss] [http://www.haskell.org/hawiki/ConalElliott ConalElliott] [http://www.haskell.org/hawiki/CraigDickson CraigDickson] [http://www.haskell.org/hawiki/CraigLennox CraigLennox] [http://www.haskell.org/hawiki/DaveMenendez DaveMenendez] [http://www.haskell.org/hawiki/DavidHimmelstrup DavidHimmelstrup] [http://www.haskell.org/hawiki/DeanHerington DeanHerington] [http://www.haskell.org/hawiki/DerekElkins DerekElkins] [http://www.haskell.org/hawiki/DiegoNavarro DiegoNavarro] [http://www.haskell.org/hawiki/DimitryGolubovsky DimitryGolubovsky] [http://www.haskell.org/hawiki/DinkoTenev DinkoTenev] [http://www.haskell.org/hawiki/DmitryAstapov DmitryAstapov] [http://www.haskell.org/hawiki/DylanThurston DylanThurston] [http://www.haskell.org/hawiki/EinarKarttunen EinarKarttunen] [http://www.haskell.org/hawiki/EndreyMark EndreyMark] [http://www.haskell.org/hawiki/FrankAtanassow FrankAtanassow] [http://www.haskell.org/hawiki/FrankChristoph FrankChristoph] [http://www.haskell.org/hawiki/FrederikEaton FrederikEaton] [http://www.haskell.org/hawiki/GaneshSittampalam GaneshSittampalam] [http://www.haskell.org/hawiki/GaneshSittampalam/MoinEditorBackup GaneshSittampalam/MoinEditorBackup] [http://www.haskell.org/hawiki/GerardHuet GerardHuet] [http://www.haskell.org/hawiki/GordonMatzigkeit GordonMatzigkeit] [http://www.haskell.org/hawiki/GraemeJefferis GraemeJefferis] [http://www.haskell.org/hawiki/GrahamKlyne GrahamKlyne] [http://www.haskell.org/hawiki/HannahSchroeter HannahSchroeter] [http://www.haskell.org/hawiki/IsaacJones IsaacJones] [http://www.haskell.org/hawiki/JackWaugh JackWaugh] [http://www.haskell.org/hawiki/JamesGray JamesGray] [http://www.haskell.org/hawiki/JanDeWit JanDeWit] [http://www.haskell.org/hawiki/JaredJennings JaredJennings] [http://www.haskell.org/hawiki/JensPetersen JensPetersen] [http://www.haskell.org/hawiki/JesperLouisAndersen JesperLouisAndersen] [http://www.haskell.org/hawiki/JesseRudolph JesseRudolph] [http://www.haskell.org/hawiki/JesseRudolph/SequenceLibrary JesseRudolph/SequenceLibrary] [http://www.haskell.org/hawiki/JohanBaltie JohanBaltie] [http://www.haskell.org/hawiki/JohannesAhlmann JohannesAhlmann] [http://www.haskell.org/hawiki/JohnHeron JohnHeron] [http://www.haskell.org/hawiki/JohnHughes JohnHughes] [http://www.haskell.org/hawiki/JohnMeacham JohnMeacham] [http://www.haskell.org/hawiki/JohnTromp JohnTromp] [http://www.haskell.org/hawiki/JoseAntonioOrtega JoseAntonioOrtega] [http://www.haskell.org/hawiki/JudeNagurney JudeNagurney] [http://www.haskell.org/hawiki/KeithWansbrough KeithWansbrough] [http://www.haskell.org/hawiki/KennethHoste KennethHoste] [http://www.haskell.org/hawiki/KetilMalde KetilMalde] [http://www.haskell.org/hawiki/LarsOlson LarsOlson] [http://www.haskell.org/hawiki/LennartKolmodin LennartKolmodin] [http://www.haskell.org/hawiki/LudvigStrigeus LudvigStrigeus] [http://www.haskell.org/hawiki/LuisAraujo LuisAraujo] [http://www.haskell.org/hawiki/LyndonTremblay LyndonTremblay] [http://www.haskell.org/hawiki/MahlenMorris MahlenMorris] [http://www.haskell.org/hawiki/ManuelChakravarty ManuelChakravarty] [http://www.haskell.org/hawiki/MarcoBakera MarcoBakera] [http://www.haskell.org/hawiki/MarkCarroll MarkCarroll] [http://www.haskell.org/hawiki/MarkWotton MarkWotton] [http://www.haskell.org/hawiki/MarkoSchuetz MarkoSchuetz] [http://www.haskell.org/hawiki/MartinNorbäck MartinNorbäck] [http://www.haskell.org/hawiki/MartinSjögren MartinSjögren] [http://www.haskell.org/hawiki/MatthewWalton MatthewWalton] [http://www.haskell.org/hawiki/MichaelRichter MichaelRichter] [http://www.haskell.org/hawiki/MikaelBrockman MikaelBrockman] [http://www.haskell.org/hawiki/MikeBeedle MikeBeedle] [http://www.haskell.org/hawiki/NoelWinstanley NoelWinstanley] [http://www.haskell.org/hawiki/OhadKammar OhadKammar] [http://www.haskell.org/hawiki/PeterSimons PeterSimons] [http://www.haskell.org/hawiki/PhilippaCowderoy PhilippaCowderoy] [http://www.haskell.org/hawiki/PhilippaCowderoy/TestPage PhilippaCowderoy/TestPage] [http://www.haskell.org/hawiki/Pishcotec Pishcotec] [http://www.haskell.org/hawiki/Qiyang Qiyang] [http://www.haskell.org/hawiki/Riastradh Riastradh] [http://www.haskell.org/hawiki/RichardTibbetts RichardTibbetts] [http://www.haskell.org/hawiki/RonLegere RonLegere] [http://www.haskell.org/hawiki/ScottTurner ScottTurner] [http://www.haskell.org/hawiki/SebastianSylvan SebastianSylvan] [http://www.haskell.org/hawiki/ShaeErisson ShaeErisson] [http://www.haskell.org/hawiki/SimonFoster SimonFoster] [http://www.haskell.org/hawiki/SimonJanes SimonJanes] [http://www.haskell.org/hawiki/SimonMarlow SimonMarlow] [http://www.haskell.org/hawiki/SpencerJanssen SpencerJanssen] [http://www.haskell.org/hawiki/StefanHeinzmann StefanHeinzmann] [http://www.haskell.org/hawiki/StefanHoldermans StefanHoldermans] [http://www.haskell.org/hawiki/StefanLjungstrand StefanLjungstrand] [http://www.haskell.org/hawiki/SteveDunham SteveDunham] [http://www.haskell.org/hawiki/SteveElkins SteveElkins] [http://www.haskell.org/hawiki/TomCooper TomCooper] [http://www.haskell.org/hawiki/TomMoertel TomMoertel] [http://www.haskell.org/hawiki/TomPledger TomPledger] [http://www.haskell.org/hawiki/VolkerStolz VolkerStolz] [http://www.haskell.org/hawiki/WadeCunningham WadeCunningham] [http://www.haskell.org/hawiki/WardCunningham WardCunningham] [http://www.haskell.org/hawiki/WegWeiser WegWeiser] [http://www.haskell.org/hawiki/WolfgangThaller WolfgangThaller] [http://www.haskell.org/hawiki/ZufallsSeite ZufallsSeite] [http://www.haskell.org/hawiki/shelarcy shelarcy]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=New_monads&diff=5349New monads2006-08-12T20:01:28Z<p>HenkJanVanTuyl: </p>
<hr />
<div>__TOC__<br />
<br />
== MonadBase ==<br />
<br />
It seems that the liftIO function from MonadIO can be generalized to access whatever the base of a transformer stack happens to be. So there is no need for a liftSTM, liftST, etc.<br />
<br />
<haskell><br />
-- By Chris Kuklewicz<br />
module MonadBase(MonadBase,liftBase,MonadIO',liftIO') where<br />
<br />
import Data.Monoid(Monoid)<br />
import Control.Monad.Trans(lift,MonadTrans)<br />
-- All the base monads with GHC<br />
import Control.Monad.ST.Strict as S(ST)<br />
import Control.Monad.ST.Lazy as L(ST)<br />
import Control.Concurrent.STM(STM)<br />
import Control.Monad.Identity(Identity)<br />
import Text.ParserCombinators.Parsec(GenParser)<br />
-- And all the MonadIO instances:<br />
import Control.Monad.List(ListT)<br />
import Control.Monad.Cont(ContT)<br />
import Control.Monad.Error(ErrorT,Error)<br />
import Control.Monad.Reader(ReaderT)<br />
import Control.Monad.State(StateT)<br />
import Control.Monad.Writer(WriterT)<br />
import Control.Monad.RWS(RWST)<br />
<br />
class (Monad m,Monad b) => MonadBase b m where<br />
liftBase :: b a -> m a<br />
<br />
-- One can recover MonadIO and liftIO from MonadBase<br />
class (MonadBase IO m) => MonadIO' m where<br />
liftIO' :: IO a -> m a<br />
liftIO' = liftBase<br />
<br />
-- Base monads<br />
instance MonadBase IO IO where liftBase = id<br />
instance MonadBase STM STM where liftBase = id<br />
instance MonadBase (GenParser tok st) (GenParser tok st) where liftBase = id<br />
instance MonadBase (S.ST s) (S.ST s) where liftBase = id<br />
instance MonadBase (L.ST s) (L.ST s) where liftBase = id<br />
instance MonadBase Maybe Maybe where liftBase = id<br />
instance MonadBase [] [] where liftBase = id<br />
instance (Error e) => MonadBase (Either e) (Either e) where liftBase = id<br />
instance MonadBase ((->) a) ((->) a) where liftBase = id<br />
instance MonadBase Identity Identity where liftBase = id<br />
<br />
-- Trans monads<br />
instance MonadBase b m => MonadBase b (ListT m) where<br />
liftBase = lift . liftBase<br />
instance MonadBase b m => MonadBase b (ContT r m) where<br />
liftBase = lift . liftBase<br />
instance (Error e, MonadBase b m) => MonadBase b (ErrorT e m) where<br />
liftBase = lift . liftBase<br />
instance MonadBase b m => MonadBase b (ReaderT r m) where<br />
liftBase = lift . liftBase<br />
instance MonadBase b m => MonadBase b (StateT s m) where<br />
liftBase = lift . liftBase<br />
instance (Monoid w, MonadBase b m) => MonadBase b (WriterT w m) where<br />
liftBase = lift . liftBase<br />
instance (Monoid w, MonadBase b m) => MonadBase b (RWST r w s m) where<br />
liftBase = lift . liftBase<br />
</haskell><br />
<br />
And an artificial example:<br />
<br />
<haskell><br />
module Main where<br />
<br />
import MonadBase<br />
import Control.Monad.Reader<br />
import Control.Monad.Writer<br />
type Foo a = (WriterT [Int] (ReaderT String [])) a<br />
<br />
foo :: Foo String<br />
foo = do<br />
x <- liftBase [1,2,3]<br />
s <- ask<br />
tell [succ x]<br />
return (s ++ show x)<br />
<br />
test = runReaderT (runWriterT foo) "hello"<br />
</haskell><br />
<br />
<pre><br />
*Main> test<br />
[("hello1",[2]),("hello2",[3]),("hello3",[4])]<br />
</pre><br />
<br />
<br />
== MonadLib ==<br />
<br />
This is by Iavor S. Diatchki and can be found at http://www.cse.ogi.edu/~diatchki/monadLib/<br />
<br />
It is a new version of the mtl package with transformers: ReaderT WriterT StateT ExceptT SearchT ContT<br />
<br />
It also defines BaseM which is like MonadBase above.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Simple_monad_examples&diff=4872Simple monad examples2006-07-17T20:45:49Z<p>HenkJanVanTuyl: Added a link to "A tour of the Haskell Monad functions"</p>
<hr />
<div>This page is designed to show some simple examples of using [[monad]]s.<br />
<br />
I personally found that I reached monad-enlightenment once I contrived this simple example while playing around to see the "guts" of a monadic expression:<br />
<br />
<haskell><br />
Just 5 >>= (\ x -> if (x == 0) then fail "zero" else Just (x + 1) )<br />
</haskell><br />
<br />
Which results in:<br />
<br />
<haskell>Just 6</haskell><br />
<br />
All you really need to know, is that the >>= operator either returns "Nothing" if it is passed "Nothing" on it's left-hand side; or if it's left-hand side is a "Just ..." it strips off the just, and passes the contents into the function supplied on it's right-hand side. Simple!<br />
<br />
Some simple exercises:<br />
<br />
What would the following snippets resolve to?<br />
<br />
<haskell><br />
Just 0 >>= (\ x -> if (x == 0) then fail "zero" else Just (x + 1) )<br />
Nothing >>= (\ x -> if (x == 0) then fail "zero" else Just (x + 1) )<br />
</haskell><br />
<br />
----<br />
More examples can be found in the reference guide [http://members.chello.nl/hjgtuyl/tourdemonad.html A tour of the Haskell Monad functions], by Henk-Jan van Tuyl.<br />
----</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Introduction_to_IO&diff=4871Introduction to IO2006-07-17T20:33:30Z<p>HenkJanVanTuyl: Added a link to "A tour of the Haskell Monad functions"</p>
<hr />
<div>''(This page is intended as a quick introduction to how IO is treated in Haskell. It doesn't describe everything there is to know, but should give some idea of how things work.)''<br />
<br />
Haskell separates pure functions from computations where side effects<br />
must be considered by encoding those side effects as values of a<br />
particular type. Specifically, a value of type <code>(IO a)</code> is an action,<br />
which if executed would produce a value of type <code>a</code>.<br />
<br />
Some examples:<br />
<haskell><br />
getLine :: IO String<br />
putStrLn :: String -> IO () -- note that the result value is an empty tuple.<br />
randomRIO :: (Random a) => (a,a) -> IO a<br />
</haskell><br />
<br />
Ordinary Haskell evaluation doesn't cause this execution to occur. A<br />
value of type <code>(IO a)</code> is almost completely inert. In fact, the only IO<br />
action which can really be said to run in a compiled Haskell program<br />
is <code>main</code>.<br />
<br />
Armed with this knowledge, we can write a "hello, world" program:<br />
<haskell><br />
main :: IO ()<br />
main = putStrLn "Hello, World!"<br />
</haskell><br />
<br />
Now, so far, all this is great, but without a way to chain actions<br />
together end-to-end, we can't do a whole lot. So Haskell provides us<br />
with a few primitives for composing and chaining together IO actions.<br />
A simple one of these is:<br />
<haskell><br />
(>>) :: IO a -> IO b -> IO b<br />
</haskell><br />
where if <code>x</code> and <code>y</code> are IO actions, then <code>(x >> y)</code> is the action that<br />
performs <code>x</code>, dropping the result, then performs <code>y</code> and returns its<br />
result.<br />
Great, we can now write programs which do multiple things:<br />
<haskell><br />
main = putStrLn "Hello" >> putStrLn "World"<br />
</haskell><br />
will print "Hello" and "World" on separate lines.<br />
<br />
However, we don't yet have a way to chain actions in which we are<br />
allowed to use the result of the first in order to affect what the<br />
second action will do. This is accomplished by the following<br />
operation, called 'bind':<br />
<haskell><br />
(>>=) :: IO a -> (a -> IO b) -> IO b<br />
</haskell><br />
Now, <code>x >>= f</code> is the action that first performs the action <code>x</code>, and captures its result, passing it to <code>f</code>, which then computes a second action to be performed. That action is then carried out, and its result is the result of the overall computation.<br />
<br />
That's a mouthful, but once you see it in use, perhaps the idea will<br />
become clearer:<br />
<haskell><br />
main = putStrLn "Hello, what is your name?"<br />
>> getLine<br />
>>= \name -> putStrLn ("Hello, " ++ name ++ "!")<br />
</haskell><br />
<br />
This is most of what we need. In fact, this bind function is really<br />
successful, and we can define <code>(>>)</code> in terms of it:<br />
<haskell><br />
x >> y = x >>= const y<br />
</haskell><br />
<br />
In practice, it turns out to also be quite important to turn a value<br />
into an IO action which does nothing, and simply returns that value.<br />
This is quite handy near the end of a chain of actions, where we want<br />
to decide what to return ourselves, rather than leaving it up to the<br />
last action in the chain. So there's one more primitive,<br />
<haskell><br />
return :: a -> IO a<br />
</haskell><br />
which does just that.<br />
<br />
You might see do-notation all over the place in real Haskell programs. In<br />
do-notation, our example program might look like:<br />
<br />
<haskell><br />
main = do putStrLn "Hello, what is your name?"<br />
name <- getLine<br />
putStrLn ("Hello, " ++ name ++ "!")<br />
</haskell><br />
<br />
This is in fact entirely equivalent to the above form, and is<br />
translated into it by the Haskell compiler. So whenever you see a do<br />
block, you can just imagine a chain of applications of <code>(>>)</code> and <code>(>>=)</code>,<br />
and some lambdas when appropriate to capture the results of actions. An action on its own on a line in a do-block will be executed, and a line of the form <code>v <- x</code> will cause the action <code>x</code> to be run, and the result bound to the variable <code>v</code>.<br />
<br />
A common mistake is to put something other than an action in the place of <code>x</code>, usually some other value. If you want to make a variable binding inside a do-block which doesn't involve running an action, then you can use a line of the form <code>let a = b</code>, which, like an ordinary let-expression will define <code>a</code> to be the same as <code>b</code>, but the definition scopes over the remainder of the do-block.<br />
<br />
Note that there is no function:<br />
<haskell><br />
unsafe :: IO a -> a<br />
</haskell><br />
as this would defeat the referential transparency of Haskell --<br />
applying <code>unsafe</code> to the same IO action might return different things<br />
every time, and Haskell functions aren't allowed to behave that way.<br />
<br />
Now, I haven't really told you anything about monads in general yet.<br />
Most monads are actually rather unlike IO, but they do share the<br />
similar concepts of bind and return. For more on monads in general,<br />
see my [[Monads as Containers]] article.<br />
<br />
- [[User:CaleGibbard|CaleGibbard]]<br />
<br />
<br />
Footnote: if you need comprehensive tutorial on using IO monad, look at the <br />
[http://haskell.org/haskellwiki/IO_inside Haskell I/O inside: Down the Rabbit's Hole]<br />
<br />
An explanation of the basic Monad functions, with examples, can be found in the reference guide [http://members.chello.nl/hjgtuyl/tourdemonad.html A tour of the Haskell Monad functions], by Henk-Jan van Tuyl.<br />
<br />
<br />
----<br />
<br />
[[Category:Idioms]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Monad&diff=4870Monad2006-07-17T19:53:35Z<p>HenkJanVanTuyl: Added a link to "A tour of the Haskell Monad functions"</p>
<hr />
<div>{{Standard class|Monad|module=Control.Monad|module-doc=Control-Monad|package=base}}<br />
The '''Monad''' class is defined like this:<br />
<br />
<haskell><br />
class Monad m where<br />
(>>=) :: m a -> (a -> m b) -> m b<br />
(>>) :: m a -> m b -> m b<br />
return :: a -> m a<br />
fail :: String -> m a<br />
</haskell><br />
<br />
All instances of Monad should obey:<br />
<br />
<haskell><br />
return a >>= k = k a<br />
m >>= return = m<br />
m >>= (\x -> k x >>= h) = (m >>= k) >>= h<br />
</haskell><br />
<br />
Any Monad can be made a [[Functor]] by defining <br />
<br />
<haskell><br />
fmap ab ma = ma >>= (return . ab)<br />
</haskell><br />
<br />
However, the Functor class is not a superclass of the Monad class. See [[Functor hierarchy proposal]].<br />
<br />
== Monad Tutorials ==<br />
<br />
Monads are known for being deeply confusing to lots of people, so there are plenty of tutorials specifically related to monads. Each takes a different approach to Monads, and hopefully everyone will find something useful.<br />
<br />
* [[Monads as Containers]]<br />
* [http://www.nomaware.com/monads/html/index.html All About Monads]<br />
* [[Simple monad examples]]<br />
* [http://www.loria.fr/~kow/monads/index.html Of monads and space suits]<br />
<br />
== Monad Reference Guides ==<br />
<br />
An explanation of the basic Monad functions, with examples, can be found in the reference guide [http://members.chello.nl/hjgtuyl/tourdemonad.html A tour of the Haskell Monad functions], by Henk-Jan van Tuyl.<br />
<br />
<br />
[[Category:Standard classes]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=IO_inside&diff=4869IO inside2006-07-17T19:34:03Z<p>HenkJanVanTuyl: Added a link to "A tour of the Haskell Monad functions"</p>
<hr />
<div>Haskell I/O has always been a source of confusion and surprises for new Haskellers. While simple I/O code in Haskell looks very similar to its equivalents in imperative languages, attempts to write somewhat more complex code often result in a total mess. This is because Haskell I/O is really very different internally. Haskell is a pure language and even the I/O system can't break this purity.<br />
<br />
The following text is an attempt to explain the details of Haskell I/O implementations. This explanation should help you eventually master all the smart I/O tricks. Moreover, I've added a detailed explanation of various traps you might encounter along the way. After reading this text, you will receive a "Master of Haskell I/O" degree that is equal to a Bachelor in Computer Science and Mathematics, simultaneously :)<br />
<br />
If you are new to Haskell I/O you may prefer to start by reading the [[Introduction to IO]] page.<br />
<br />
<br />
== Haskell is a pure language ==<br />
<br />
Haskell is a pure language, which means that the result of any function call is fully determined by its arguments. Pseudo-functions like rand() or getchar() in C, which return different results on each call, are simply impossible to write in Haskell. Moreover, Haskell functions can't have side effects, which means that they can't effect any changes to the "real world", like changing files, writing to the screen, printing, sending data over the network, and so on. These two restrictions together mean that any function<br />
call can be omitted, repeated, or replaced by the result of a previous call with the same parameters, and the language '''guarantees''' that all these rearrangements will not change the program result!<br />
<br />
Let's compare this to C: optimizing C compilers try to guess which functions have no side effects and don't depend on mutable global variables. If this guess is wrong, an optimization can change the program's semantics! To avoid this kind of disaster, C optimizers are conservative in their guesses or require hints from the programmer about the purity of functions.<br />
<br />
Compared to an optimizing C compiler, a Haskell compiler is a set of pure mathematical transformations. This results in much better high-level optimization facilities. Moreover, pure mathematical computations can be much more easily divided into several threads that may be executed in parallel, which is increasingly important in these days of multi-core CPUs. Finally, pure computations are less error-prone and easier to verify, which adds to Haskell's robustness and to the speed of program development using Haskell.<br />
<br />
This purity creates other problems, though: how we can do I/O or work with stateful algorithms and side effects in a pure language? This question has had many different solutions proposed in 18 years of Haskell development, though a solution based on '''''monads''''' is now the standard.<br />
<br />
<br />
<br />
== What is a monad? ==<br />
<br />
What is a monad? It's something from mathematical category theory, which I<br />
don't know anymore :) In order to understand how monads are used to<br />
solve the problem of I/O and side effects, you don't need to know it. It's<br />
enough to just know elementary mathematics, like I do :)<br />
<br />
Let's imagine that we want to implement in Haskell the well-known<br />
'getchar' function. What type should it have? Let's try:<br />
<br />
<haskell><br />
getchar :: Char<br />
<br />
get2chars = [getchar,getchar]<br />
</haskell><br />
<br />
What will we get with 'getchar' having just the 'Char' type? You can see<br />
all the possible problems in the definition of 'get2chars':<br />
<br />
# Because the Haskell compiler treats all functions as pure (not having side effects), it can avoid "excessive" calls to 'getchar' and use one returned value twice.<br />
# Even if it does make two calls, there is no way to determine which call should be performed first. Do you want to return the two chars in the order in which they were read, or in the opposite order? Nothing in the definition of 'get2chars' answers this question.<br />
<br />
How can these problems be solved, from the programmer's viewpoint?<br />
Let's introduce a fake parameter of 'getchar' to make each call<br />
"different" from the compiler's point of view:<br />
<br />
<haskell><br />
getchar :: Int -> Char<br />
<br />
get2chars = [getchar 1, getchar 2]<br />
</haskell><br />
<br />
Right away, this solves the first problem mentioned above - now the<br />
compiler will make two calls because it sees them as having different<br />
parameters. The whole 'get2chars' function should also have a<br />
fake parameter, otherwise we will have the same problem calling it:<br />
<br />
<haskell><br />
getchar :: Int -> Char<br />
get2chars :: Int -> String<br />
<br />
get2chars _ = [getchar 1, getchar 2]<br />
</haskell><br />
<br />
<br />
Now we need to give the compiler some clue to determine which function it<br />
should call first. The Haskell language doesn't provide any way to express<br />
order of evaluation... except for data dependencies! How about adding an<br />
artificial data dependency which prevents evaluation of the second<br />
'getchar' before the first one? In order to achieve this, we will<br />
return an additional fake result from 'getchar' that will be used as a<br />
parameter for the next 'getchar' call:<br />
<br />
<haskell><br />
getchar :: Int -> (Char, Int)<br />
<br />
get2chars _ = [a,b] where (a,i) = getchar 1<br />
(b,_) = getchar i<br />
</haskell><br />
<br />
So far so good - now we can guarantee that 'a' is read before 'b'<br />
because reading 'b' needs the value ('i') that is returned by reading 'a'!<br />
<br />
We've added a fake parameter to 'get2chars' but the problem is that the<br />
Haskell compiler is too smart! It can believe that the external 'getchar'<br />
function is really dependent on its parameter but for 'get2chars' it<br />
will see that we're just cheating because we throw it away! Therefore it won't feel obliged to execute the calls in the order we want. How can we fix this? How about passing this fake parameter to the 'getchar' function?! In this case<br />
the compiler can't guess that it is really unused :)<br />
<br />
<haskell><br />
get2chars i0 = [a,b] where (a,i1) = getchar i0<br />
(b,i2) = getchar i1<br />
</haskell><br />
<br />
<br />
And more - 'get2chars' has all the same purity problems as the 'getchar'<br />
function. If you need to call it two times, you need a way to describe<br />
the order of these calls. Look at:<br />
<br />
<haskell><br />
get4chars = [get2chars 1, get2chars 2] -- order of 'get2chars' calls isn't defined<br />
</haskell><br />
<br />
We already know how to deal with these problems - 'get2chars' should<br />
also return some fake value that can be used to order calls:<br />
<br />
<haskell><br />
get2chars :: Int -> (String, Int)<br />
<br />
get4chars i0 = (a++b) where (a,i1) = get2chars i0<br />
(b,i2) = get2chars i1<br />
</haskell><br />
<br />
<br />
But what's the fake value 'get2chars' should return? If we use some integer constant, the excessively-smart Haskell compiler will guess that we're cheating again :) What about returning the value returned by 'getchar'? See:<br />
<br />
<haskell><br />
get2chars :: Int -> (String, Int)<br />
get2chars i0 = ([a,b], i2) where (a,i1) = getchar i0<br />
(b,i2) = getchar i1<br />
</haskell><br />
<br />
Believe it or not, but we've just constructed the whole "monadic"<br />
Haskell I/O system.<br />
<br />
<br />
<br />
== Welcome to the RealWorld, baby :) ==<br />
<br />
The 'main' Haskell function has the type:<br />
<br />
<haskell><br />
main :: RealWorld -> ((), RealWorld)<br />
</haskell><br />
<br />
where 'RealWorld' is a fake type used instead of our Int. It's something<br />
like the baton passed in a relay race. When 'main' calls some IO function,<br />
it passes the "RealWorld" it received as a parameter. All IO functions have<br />
similar types involving RealWorld as a parameter and result. To be<br />
exact, "IO" is a type synonym defined in the following way:<br />
<br />
<haskell><br />
type IO a = RealWorld -> (a, RealWorld)<br />
</haskell><br />
<br />
So, 'main' just has type "IO ()", 'getChar' has type "IO Char" and so<br />
on. You can think of the type "IO Char" as meaning "take the current RealWorld, do something to it, and return a Char and a (possibly changed) RealWorld". Let's look at 'main' calling 'getChar' two times:<br />
<br />
<haskell><br />
getChar :: RealWorld -> (Char, RealWorld)<br />
<br />
main :: RealWorld -> ((), RealWorld)<br />
main world0 = let (a, world1) = getChar world0<br />
(b, world2) = getChar world1<br />
in ((), world2)<br />
</haskell><br />
<br />
<br />
Look at this closely: 'main' passes to first 'getChar' the "world" it<br />
received. This 'getChar' returns some new value of type RealWorld<br />
that gets used in the next call. Finally, 'main' returns the "world" it got<br />
from the second 'getChar'.<br />
<br />
# Is it possible here to omit any call of 'getChar' if the Char it read is not used? No, because we need to return the "world" that is the result of the second 'getChar' and this in turn requires the "world" returned from the first 'getChar'.<br />
# Is it possible to reorder the 'getChar' calls? No: the second 'getChar' can't be called before the first one because it uses the "world" returned from the first call.<br />
# Is it possible to duplicate calls? In Haskell semantics - yes, but real compilers never duplicate work in such simple cases (otherwise, the programs generated will not have any speed guarantees).<br />
<br />
<br />
As we already said, RealWorld values are used like a baton which gets passed<br />
between all routines called by 'main' in strict order. Inside each<br />
routine called, RealWorld values are used in the same way. Overall, in<br />
order to "compute" the world to be returned from 'main', we should perform<br />
each IO procedure that is called from 'main', directly or indirectly.<br />
This means that each procedure inserted in the chain will be performed<br />
just at the moment (relative to the other IO actions) when we intended it<br />
to be called. Let's consider the following program:<br />
<br />
<haskell><br />
main = do a <- ask "What is your name?"<br />
b <- ask "How old are you?"<br />
return ()<br />
<br />
ask s = do putStr s<br />
readLn<br />
</haskell><br />
<br />
Now you have enough knowledge to rewrite it in a low-level way and<br />
check that each operation that should be performed will really be<br />
performed with the arguments it should have and in the order we expect.<br />
<br />
<br />
But what about conditional execution? No problem. Let's define the<br />
well-known 'when' operation:<br />
<br />
<haskell><br />
when :: Bool -> IO () -> IO ()<br />
when condition action world =<br />
if condition<br />
then action world<br />
else ((), world)<br />
</haskell><br />
<br />
As you can see, we can easily include or exclude from the execution chain<br />
IO procedures (actions) depending on the data values. If 'condition'<br />
will be False on the call of 'when', 'action' will never be called because<br />
real Haskell compilers, again, never call functions whose results<br />
are not required to calculate the final result (''i.e.'', here, the final "world" value of 'main').<br />
<br />
Loops and more complex control structures can be implemented in<br />
the same way. Try it as an exercise!<br />
<br />
<br />
Finally, you may want to know how much passing these RealWorld<br />
values around the program costs. It's free! These fake values exist solely for the compiler while it analyzes and optimizes the code, but when it gets to assembly code generation, it "suddenly" realize that this type is like "()", so<br />
all these parameters and result values can be omitted from the final generated code. Isn't it beautiful? :)<br />
<br />
<br />
<br />
== '>>=' and 'do' notation ==<br />
<br />
All beginners (including me :)) start by thinking that 'do' is some<br />
magic statement that executes IO actions. That's wrong - 'do' is just<br />
syntactic sugar that simplifies the writing of procedures that use IO (and also other monads, but that's beyond the scope of this tutorial). 'do' notation eventually gets translated to statements passing "world" values around like we've manually written above and is used to simplify the gluing of several<br />
IO actions together. You don't need to use 'do' for just one statement; for instance,<br />
<br />
<haskell><br />
main = do putStr "Hello!"<br />
</haskell><br />
<br />
is desugared to:<br />
<br />
<haskell><br />
main = putStr "Hello!"<br />
</haskell><br />
<br />
But nevertheless it's considered Good Style to use 'do' even for one statement<br />
because it simplifies adding new statements in the future.<br />
<br />
<br />
Let's examine how to desugar a 'do' with multiple statements in the<br />
following example: <br />
<br />
<haskell><br />
main = do putStr "What is your name?"<br />
putStr "How old are you?"<br />
putStr "Nice day!"<br />
</haskell><br />
<br />
The 'do' statement here just joins several IO actions that should be<br />
performed sequentially. It's translated to sequential applications<br />
of one of the so-called "binding operators", namely '>>':<br />
<br />
<haskell><br />
main = (putStr "What is your name?")<br />
>> ( (putStr "How old are you?")<br />
>> (putStr "Nice day!")<br />
)<br />
</haskell><br />
<br />
This binding operator just combines two IO actions, executing them<br />
sequentially by passing the "world" between them:<br />
<br />
<haskell><br />
(>>) :: IO a -> IO b -> IO b<br />
(action1 >> action2) world0 =<br />
let (a, world1) = action1 world0<br />
(b, world2) = action2 world1<br />
in (b, world2)<br />
</haskell><br />
<br />
If defining operators this way looks strange to you, read this<br />
definition as follows:<br />
<br />
<haskell><br />
action1 >> action2 = action<br />
where<br />
action world0 = let (a, world1) = action1 world0<br />
(b, world2) = action2 world1<br />
in (b, world2)<br />
</haskell><br />
<br />
Now you can substitute the definition of '>>' at the places of its usage<br />
and check that program constructed by the 'do' desugaring is actually the<br />
same as we could write by manually manipulating "world" values.<br />
<br />
<br />
A more complex example involves the binding of variables using "<-":<br />
<br />
<haskell><br />
main = do a <- readLn<br />
print a<br />
</haskell><br />
<br />
This code is desugared into:<br />
<br />
<haskell><br />
main = readLn<br />
>>= (\a -> print a)<br />
</haskell><br />
<br />
As you should remember, the '>>' binding operator silently ignores<br />
the value of its first action and returns as an overall result<br />
the result of its second action only. On the other hand, the '>>=' binding operator (note the extra '=' at the end) allows us to use the result of its first action - it gets passed as an additional parameter to the second one! Look at the definition:<br />
<br />
<haskell><br />
(>>=) :: IO a -> (a -> IO b) -> IO b<br />
(action1 >>= action2) world0 =<br />
let (a, world1) = action1 world0<br />
(b, world2) = action2 a world1<br />
in (b, world2)<br />
</haskell><br />
<br />
First, what does the type of the second "action" (more precisely, a function which returns an IO action), namely "a -> IO b", mean? By<br />
substituting the "IO" definition, we get "a -> RealWorld -> (b, RealWorld)".<br />
This means that second action actually has two parameters<br />
- the type 'a' actually used inside it, and the value of type RealWorld used for sequencing of IO actions. That's always the case - any IO procedure has one<br />
more parameter compared to what you see in its type signature. This<br />
parameter is hidden inside the definition of the type alias "IO".<br />
<br />
Second, you can use these '>>' and '>>=' operations to simplify your<br />
program. For example, in the code above we don't need to introduce the<br />
variable, because the result of 'readLn' can be send directly to 'print':<br />
<br />
<haskell><br />
main = readLn >>= print<br />
</haskell><br />
<br />
<br />
And third - as you see, the notation:<br />
<br />
<haskell><br />
do x <- action1<br />
action2<br />
</haskell><br />
<br />
where 'action1' has type "IO a" and 'action2' has type "IO b",<br />
translates into:<br />
<br />
<haskell><br />
action1 >>= (\x -> action2)<br />
</haskell><br />
<br />
where the second argument of '>>=' has the type "a -> IO b". It's the way<br />
the '<-' binding is processed - the name on the left-hand side of '<-' just becomes a parameter of subsequent operations represented as one large IO action. Note also that if 'action1' has type "IO a" then 'x' will just have type "a"; you can think of the effect of '<-' as "unpacking" the IO value of 'action1' into 'x'. Note also that '<-' is not a true operator; it's pure syntax, just like 'do' itself. Its meaning results only from the way it gets desugared.<br />
<br />
Look at the next example: <br />
<br />
<haskell><br />
main = do putStr "What is your name?"<br />
a <- readLn<br />
putStr "How old are you?"<br />
b <- readLn<br />
print (a,b)<br />
</haskell><br />
<br />
This code is desugared into:<br />
<br />
<haskell><br />
main = putStr "What is your name?"<br />
>> readLn<br />
>>= \a -> putStr "How old are you?"<br />
>> readLn<br />
>>= \b -> print (a,b)<br />
</haskell><br />
<br />
I omitted the parentheses here; both the '>>' and the '>>=' operators are<br />
left-associative, which means that the 'a' and 'b' bindings introduced<br />
here are valid for all remaining actions. As an exercise, add the<br />
parentheses yourself and translate this procedure into the low-level<br />
code that explicitly passes "world" values. I think it should be enough to help you finally realize how the 'do' translation and binding operators work.<br />
<br />
<br />
Oh, no! I forgot the third monadic operator - 'return'. It just<br />
combines its two parameters - the value passed and "world":<br />
<br />
<haskell><br />
return :: a -> IO a<br />
return a world0 = (a, world0)<br />
</haskell><br />
<br />
How about translating a simple example of 'return' usage? Say,<br />
<br />
<haskell><br />
main = do a <- readLn<br />
return (a*2)<br />
</haskell><br />
<br />
<br />
Programmers with an imperative language background often think that<br />
'return' in Haskell, as in other languages, immediately returns from<br />
the IO procedure. As you can see in its definition (and even just from its<br />
type!), such an assumption is totally wrong. The only purpose of using<br />
'return' is to "lift" some value (of type 'a') into the result of<br />
a whole action (of type "IO a") and therefore it should generally be used only as the last executed statement of some IO sequence. For example try to<br />
translate the following procedure into the corresponding low-level code:<br />
<br />
<haskell><br />
main = do a <- readLn<br />
when (a>=0) $ do<br />
return ()<br />
print "a is negative"<br />
</haskell><br />
<br />
and you will realize that the 'print' statement is executed even for non-negative values of 'a'. If you need to escape from the middle of an IO procedure, you can use the 'if' statement:<br />
<br />
<haskell><br />
main = do a <- readLn<br />
if (a>=0)<br />
then return ()<br />
else print "a is negative"<br />
</haskell><br />
<br />
Moreover, Haskell layout rules allow us to use the following layout:<br />
<br />
<haskell><br />
main = do a <- readLn<br />
if (a>=0) then return ()<br />
else do<br />
print "a is negative"<br />
...<br />
</haskell><br />
<br />
that may be useful for escaping from the middle of a longish 'do' statement.<br />
<br />
<br />
Last exercise: implement a function 'liftM' that lifts operations on<br />
plain values to the operations on monadic ones. Its type signature:<br />
<br />
<haskell><br />
liftM :: (a -> b) -> (IO a -> IO b)<br />
</haskell><br />
<br />
If that's too hard for you, start with the following high-level<br />
definition and rewrite it in low-level fashion:<br />
<br />
<haskell><br />
liftM f action = do x <- action<br />
return (f x)<br />
</haskell><br />
<br />
<br />
<br />
== Mutable data (references, arrays, hash tables...) ==<br />
<br />
As you should know, every name in Haskell is bound to one fixed (immutable) value. This greatly simplifies understanding algorithms and code optimization, but it's inappropriate in some cases. As we all know, there are plenty of algorithms that are simpler to implement in terms of updatable<br />
variables, arrays and so on. This means that the value associated with<br />
a variable, for example, can be different at different execution points,<br />
so reading its value can't be considered as a pure function. Imagine,<br />
for example, the following code:<br />
<br />
<haskell><br />
main = do let a0 = readVariable varA<br />
_ = writeVariable varA 1<br />
a1 = readVariable varA<br />
print (a0, a1)<br />
</haskell><br />
<br />
Does this look strange? First, the two calls to 'readVariable' look the same, so the compiler can just reuse the value returned by the first call. Second,<br />
the result of the 'writeVariable' call isn't used so the compiler can (and will!) omit this call completely. To complete the picture, these three calls may be rearranged in any order because they appear to be independent of each<br />
other. This is obviously not what was intended. What's the solution? You already know this - use IO actions! Using IO actions guarantees that:<br />
<br />
# the execution order will be retained as written<br />
# each action will have to be executed<br />
# the result of the "same" action (such as "readVariable varA") will not be reused<br />
<br />
So, the code above really should be written as:<br />
<br />
<haskell><br />
main = do varA <- newIORef 0 -- Create and initialize a new variable<br />
a0 <- readIORef varA<br />
writeIORef varA 1<br />
a1 <- readIORef varA<br />
print (a0, a1)<br />
</haskell><br />
<br />
Here, 'varA' has the type "IORef Int" which means "a variable (reference) in<br />
the IO monad holding a value of type Int". newIORef creates a new variable<br />
(reference) and returns it, and then read/write actions use this<br />
reference. The value returned by the "readIORef varA" action depends not<br />
only on the variable involved but also on the moment this operation is performed so it can return different values on each call.<br />
<br />
Arrays, hash tables and any other _mutable_ data structures are<br />
defined in the same way - for each of them, there's an operation that creates new "mutable values" and returns a reference to it. Then special read and write<br />
operations in the IO monad are used. The following code shows an example<br />
using mutable arrays:<br />
<br />
<haskell><br />
import Data.Array.IO<br />
main = do arr <- newArray (1,10) 37 :: IO (IOArray Int Int)<br />
a <- readArray arr 1<br />
writeArray arr 1 64<br />
b <- readArray arr 1<br />
print (a, b)<br />
</haskell><br />
<br />
Here, an array of 10 elements with 37 as the initial value at each location is created. After reading the value of the first element (index 1) into 'a' this element's value is changed to 64 and then read again into 'b'. As you can see by executing this code, 'a' will be set to 37 and 'b' to 64.<br />
<br />
<br />
<br />
Other state-dependent operations are also often implemented as IO<br />
actions. For example, a random number generator should return a different<br />
value on each call. It looks natural to give it a type involving IO:<br />
<br />
<haskell><br />
rand :: IO Int<br />
</haskell><br />
<br />
Moreover, when you import C routines you should be careful - if this<br />
routine is impure, i.e. its result depends on something in the "real<br />
world" (file system, memory contents...), internal state and so on,<br />
you should give it an IO type. Otherwise, the compiler can<br />
"optimize" repetitive calls of this procedure with the same parameters! :)<br />
<br />
For example, we can write a non-IO type for:<br />
<br />
<haskell><br />
foreign import ccall<br />
sin :: Double -> Double<br />
</haskell><br />
<br />
because the result of 'sin' depends only on its argument, but<br />
<br />
<haskell><br />
foreign import ccall<br />
tell :: Int -> IO Int<br />
</haskell><br />
<br />
If you will declare 'tell' as a pure function (without IO) then you may<br />
get the same position on each call! :)<br />
<br />
== IO actions as values ==<br />
<br />
By this point you should understand why it's impossible to use IO<br />
actions inside non-IO (pure) procedures. Such procedures just don't<br />
get a "baton"; they don't know any "world" value to pass to an IO action.<br />
The RealWorld type is an abstract datatype, so pure functions also can't construct RealWorld values by themselves, and it's a strict type, so 'undefined' also can't be used. So, the prohibition of using IO actions inside pure procedures is just a type system trick (as it usually is in Haskell :)).<br />
<br />
But while pure code can't _execute_ IO actions, it can work with them<br />
as with any other functional values - they can be stored in data<br />
structures, passed as parameters, returned as results, collected in<br />
lists, and partially applied. But an IO action will remain a<br />
functional value because we can't apply it to the last argument - of<br />
type RealWorld.<br />
<br />
In order to _execute_ the IO action we need to apply it to some<br />
RealWorld value. That can be done only inside some IO procedure,<br />
in its "actions chain". And real execution of this action will take<br />
place only when this procedure is called as part of the process of<br />
"calculating the final value of world" for 'main'. Look at this example:<br />
<br />
<haskell><br />
main world0 = let get2chars = getChar >> getChar<br />
((), world1) = putStr "Press two keys" world0<br />
(answer, world2) = get2chars world1<br />
in ((), world2)<br />
</haskell><br />
<br />
Here we first bind a value to 'get2chars' and then write a binding<br />
involving 'putStr'. But what's the execution order? It's not defined<br />
by the order of the 'let' bindings, it's defined by the order of processing<br />
"world" values! You can arbitrarily reorder the binding statements - the execution order will be defined by the data dependency with respect to the <br />
"world" values that get passed around. Let's see what this 'main' looks like in the 'do' notation:<br />
<br />
<haskell><br />
main = do let get2chars = getChar >> getChar<br />
putStr "Press two keys"<br />
get2chars<br />
return ()<br />
</haskell><br />
<br />
As you can see, we've eliminated two of the 'let' bindings and left only the one defining 'get2chars'. The non-'let' statements are executed in the exact order in which they're written, because they pass the "world" value from statement to statement as we described above. Thus, this version of the function is much easier to understand because we don't have to mentally figure out the data dependency of the "world" value.<br />
<br />
Moreover, IO actions like 'get2chars' can't be executed directly<br />
because they are functions with a RealWorld parameter. To execute them,<br />
we need to supply the RealWorld parameter, i.e. insert them in the 'main'<br />
chain, placing them in some 'do' sequence executed from 'main' (either directly in the 'main' function, or indirectly in an IO function called from 'main'). Until that's done, they will remain like any function, in partially<br />
evaluated form. And we can work with IO actions as with any other<br />
functions - bind them to names (as we did above), save them in data<br />
structures, pass them as function parameters and return them as results - and<br />
they won't be performed until you give them the magic RealWorld<br />
parameter!<br />
<br />
<br />
<br />
=== Example: a list of IO actions ===<br />
<br />
Let's try defining a list of IO actions:<br />
<br />
<haskell><br />
ioActions :: [IO ()]<br />
ioActions = [(print "Hello!"),<br />
(putStr "just kidding"),<br />
(getChar >> return ())<br />
]<br />
</haskell><br />
<br />
I used additional parentheses around each action, although they aren't really required. If you still can't believe that these actions won't be executed immediately, just recall the real type of this list:<br />
<br />
<haskell><br />
ioActions :: [RealWorld -> ((), RealWorld)]<br />
</haskell><br />
<br />
Well, now we want to execute some of these actions. No problem, just<br />
insert them into the 'main' chain:<br />
<br />
<haskell><br />
main = do head ioActions<br />
ioActions !! 1<br />
last ioActions<br />
</haskell><br />
<br />
Looks strange, right? :) Really, any IO action that you write in a 'do'<br />
statement (or use as a parameter for the '>>'/'>>=' operators) is an expression<br />
returning a result of type 'IO a' for some type 'a'. Typically, you use some function that has the type 'x -> y -> ... -> IO a' and provide all the x, y, etc. parameters. But you're not limited to this standard scenario -<br />
don't forget that Haskell is a functional language and you're free to<br />
compute the functional value required (recall that "IO a" is really a function<br />
type) in any possible way. Here we just extracted several functions<br />
from the list - no problem. This functional value can also be<br />
constructed on-the-fly, as we've done in the previous example - that's also<br />
OK. Want to see this functional value passed as a parameter?<br />
Just look at the definition of 'when'. Hey, we can buy, sell, and rent<br />
these IO actions just like we can with any other functional values! For example, let's define a function that executes all the IO actions in the list:<br />
<br />
<haskell><br />
sequence_ :: [IO a] -> IO ()<br />
sequence_ [] = return ()<br />
sequence_ (x:xs) = do x<br />
sequence_ xs<br />
</haskell><br />
<br />
No black magic - we just extract IO actions from the list and insert<br />
them into a chain of IO operations that should be performed one after another (in the same order that they occurred in the list) to "compute the final world value" of the entire 'sequence_' call.<br />
<br />
With the help of 'sequence_', we can rewrite our last 'main' function as:<br />
<br />
<haskell><br />
main = sequence_ ioActions<br />
</haskell><br />
<br />
<br />
Haskell's ability to work with IO actions as with any other<br />
(functional and non-functional) values allows us to define control<br />
structures of arbitrary complexity. Try, for example, to define a control<br />
structure that repeats an action until it returns the 'False' result:<br />
<br />
<haskell><br />
while :: IO Bool -> IO ()<br />
while action = ???<br />
</haskell><br />
<br />
Most programming languages don't allow you to define control structures at all, and those that do often require you to use a macro-expansion system. In Haskell, control structures are just trivial functions anyone can write.<br />
<br />
<br />
=== Example: returning an IO action as a result ===<br />
<br />
How about returning an IO action as the result of a function? Well, we've done<br />
this each time we've defined an IO procedure - they all return IO actions<br />
that need a RealWorld value to be performed. While we usually just<br />
execute them as part of a higher-level IO procedure, it's also<br />
possible to just collect them without actual execution:<br />
<br />
<haskell><br />
main = do let a = sequence ioActions<br />
b = when True getChar<br />
c = getChar >> getChar<br />
putStr "These 'let' statements are not executed!"<br />
</haskell><br />
<br />
These assigned IO procedures can be used as parameters to other<br />
procedures, or written to global variables, or processed in some other<br />
way, or just executed later, as we did in the example with 'get2chars'.<br />
<br />
But how about returning a parameterized IO action from an IO procedure? Let's define a procedure that returns the i'th byte from a file represented as a Handle:<br />
<br />
<haskell><br />
readi h i = do hSeek h i AbsoluteSeek<br />
hGetChar h<br />
</haskell><br />
<br />
So far so good. But how about a procedure that returns the i'th byte of a file<br />
with a given name without reopening it each time?<br />
<br />
<haskell><br />
readfilei :: String -> IO (Integer -> IO Char)<br />
readfilei name = do h <- openFile name ReadMode<br />
return (readi h)<br />
</haskell><br />
<br />
As you can see, it's an IO procedure that opens a file and returns...<br />
another IO procedure that will read the specified byte. But we can go<br />
further and include the 'readi' body in 'readfilei':<br />
<br />
<haskell><br />
readfilei name = do h <- openFile name ReadMode<br />
let readi h i = do hSeek h i AbsoluteSeek<br />
hGetChar h<br />
return (readi h)<br />
</haskell><br />
<br />
That's a little better. But why do we add 'h' as a parameter to 'readi' if it can be obtained from the environment where 'readi' is now defined? An even shorter version is this:<br />
<br />
<haskell><br />
readfilei name = do h <- openFile name ReadMode<br />
let readi i = do hSeek h i AbsoluteSeek<br />
hGetChar h<br />
return readi<br />
</haskell><br />
<br />
What have we done here? We've build a parameterized IO action involving local<br />
names inside 'readfilei' and returned it as the result. Now it can be<br />
used in the following way:<br />
<br />
<haskell><br />
main = do myfile <- readfilei "test"<br />
a <- myfile 0<br />
b <- myfile 1<br />
print (a,b)<br />
</haskell><br />
<br />
<br />
This way of using IO actions is very typical for Haskell programs - you<br />
just construct one or more IO actions that you need,<br />
with or without parameters, possibly involving the parameters that your<br />
"constructor" received, and return them to the caller. Then these IO actions<br />
can be used in the rest of the program without any knowledge about your<br />
internal implementation strategy. One thing this can be used for is to<br />
partially emulate the OOP (or more precisely, the ADT) programming paradigm.<br />
<br />
<br />
=== Example: a memory allocator generator ===<br />
<br />
As an example, one of my programs has a module which is a memory suballocator. It receives the address and size of a large memory block and returns two<br />
procedures - one to allocate a subblock of a given size and the other to<br />
free the allocated subblock:<br />
<br />
<haskell><br />
memoryAllocator :: Ptr a -> Int -> IO (Int -> IO (Ptr b),<br />
Ptr c -> IO ())<br />
<br />
memoryAllocator buf size = do ......<br />
let alloc size = do ...<br />
...<br />
free ptr = do ...<br />
...<br />
return (alloc, free)<br />
</haskell><br />
<br />
How this is implemented? 'alloc' and 'free' work with references<br />
created inside the memoryAllocator procedure. Because the creation of these references is a part of the memoryAllocator IO actions chain, a new independent set of references will be created for each memory block for which<br />
memoryAllocator is called:<br />
<br />
<haskell><br />
memoryAllocator buf size = do start <- newIORef buf<br />
end <- newIORef (buf `plusPtr` size)<br />
...<br />
</haskell><br />
<br />
These two references are read and written in the 'alloc' and 'free' definitions (we'll implement a very simple memory allocator for this example):<br />
<br />
<haskell><br />
...<br />
let alloc size = do addr <- readIORef start<br />
writeIORef start (addr `plusPtr` size)<br />
return addr<br />
<br />
let free ptr = do writeIORef start ptr<br />
</haskell><br />
<br />
What we've defined here is just a pair of closures that use state<br />
available at the moment of their definition. As you can see, it's as<br />
easy as in any other functional language, despite Haskell's lack<br />
of direct support for impure functions.<br />
<br />
The following example uses procedures, returned by memoryAllocator, to<br />
simultaneously allocate/free blocks in two independent memory buffers:<br />
<br />
<haskell><br />
main = do buf1 <- mallocBytes (2^16)<br />
buf2 <- mallocBytes (2^20)<br />
(alloc1, free1) <- memoryAllocator buf1 (2^16)<br />
(alloc2, free2) <- memoryAllocator buf2 (2^20)<br />
ptr11 <- alloc1 100<br />
ptr21 <- alloc2 1000<br />
free1 ptr11<br />
free2 ptr21<br />
ptr12 <- alloc1 100<br />
ptr22 <- alloc2 1000<br />
</haskell><br />
<br />
<br />
<br />
=== Example: emulating OOP with record types ===<br />
<br />
Let's implement the classical OOP example: drawing figures. There are<br />
figures of different types: circles, rectangles and so on. The task is<br />
to create a heterogeneous list of figures. All figures in this list should<br />
support the same set of operations: draw, move and so on. We will<br />
represent these operations as IO procedures. Instead of a "class" let's<br />
define a structure containing implementations of all the procedures<br />
required:<br />
<br />
<haskell><br />
data Figure = Figure { draw :: IO (),<br />
move :: Displacement -> IO ()<br />
}<br />
<br />
type Displacement = (Int, Int) -- horizontal and vertical displacement in points<br />
</haskell><br />
<br />
<br />
The constructor of each figure's type should just return a Figure record:<br />
<br />
<haskell><br />
circle :: Point -> Radius -> IO Figure<br />
rectangle :: Point -> Point -> IO Figure<br />
<br />
type Point = (Int, Int) -- point coordinates<br />
type Radius = Int -- circle radius in points<br />
</haskell><br />
<br />
<br />
We will "draw" figures by just printing their current parameters.<br />
Let's start with a simplified implementation of the 'circle' and 'rectangle'<br />
constructors, without actual 'move' support:<br />
<br />
<haskell><br />
circle center radius = do<br />
let description = " Circle at "++show center++" with radius "++show radius<br />
return $ Figure { draw = putStrLn description }<br />
<br />
rectangle from to = do<br />
let description = " Rectangle "++show from++"-"++show to)<br />
return $ Figure { draw = putStrLn description }<br />
</haskell><br />
<br />
<br />
As you see, each constructor just returns a fixed 'draw' procedure that prints<br />
parameters with which the concrete figure was created. Let's test it:<br />
<br />
<haskell><br />
drawAll :: [Figure] -> IO ()<br />
drawAll figures = do putStrLn "Drawing figures:"<br />
mapM_ draw figures<br />
<br />
main = do figures <- sequence [circle (10,10) 5,<br />
circle (20,20) 3,<br />
rectangle (10,10) (20,20),<br />
rectangle (15,15) (40,40)]<br />
drawAll figures<br />
</haskell><br />
<br />
<br />
Now let's define "full-featured" figures that can actually be<br />
moved around. In order to achieve this, we should provide each figure<br />
with a mutable variable that holds each figure's current screen location. The<br />
type of this variable will be "IORef Point". This variable should be created in the figure constructor and manipulated in IO procedures (closures) enclosed in<br />
the Figure record:<br />
<br />
<haskell><br />
circle center radius = do<br />
centerVar <- newIORef center<br />
<br />
let drawF = do center <- readIORef centerVar<br />
putStrLn (" Circle at "++show center<br />
++" with radius "++show radius)<br />
<br />
let moveF (addX,addY) = do (x,y) <- readIORef centerVar<br />
writeIORef centerVar (x+addX, y+addY)<br />
<br />
return $ Figure { draw=drawF, move=moveF }<br />
<br />
<br />
rectangle from to = do<br />
fromVar <- newIORef from<br />
toVar <- newIORef to<br />
<br />
let drawF = do from <- readIORef fromVar<br />
to <- readIORef toVar<br />
putStrLn (" Rectangle "++show from++"-"++show to)<br />
<br />
let moveF (addX,addY) = do (fromX,fromY) <- readIORef fromVar<br />
(toX,toY) <- readIORef toVar<br />
writeIORef fromVar (fromX+addX, fromY+addY)<br />
writeIORef toVar (toX+addX, toY+addY)<br />
<br />
return $ Figure { draw=drawF, move=moveF }<br />
</haskell><br />
<br />
<br />
Now we can test the code which moves figures around:<br />
<br />
<haskell><br />
main = do figures <- sequence [circle (10,10) 5,<br />
rectangle (10,10) (20,20)]<br />
drawAll figures<br />
mapM_ (\fig -> move fig (10,10)) figures<br />
drawAll figures<br />
</haskell><br />
<br />
<br />
It's important to realize that we are not limited to including only IO actions<br />
in a record that's intended to simulate a C++/Java-style interface. The record can also include values, IORefs, pure functions - in short, any type of data. For example, we can easily add to the Figure interface fields for area and origin:<br />
<br />
<haskell><br />
data Figure = Figure { draw :: IO (),<br />
move :: Displacement -> IO (),<br />
area :: Double,<br />
origin :: IORef Point<br />
}<br />
</haskell><br />
<br />
<br />
<br />
== unsafePerformIO and unsafeInterleaveIO ==<br />
<br />
Programmers coming from an imperative language background often look for a way to execute IO actions inside a pure procedure. But what does this mean?<br />
Imagine that you're trying to write a procedure that reads the contents of a file with a given name, and you try to write it as a pure (non-IO) function:<br />
<br />
<haskell><br />
readContents :: Filename -> String<br />
</haskell><br />
<br />
Defining readContents as a pure function will certainly simplify the code that uses it. But it will also create problems for the compiler:<br />
<br />
# This call is not inserted in a sequence of "world transformations", so the compiler doesn't know at what exact moment you want to execute this action. For example, if the file has one kind of contents at the beginning of the program and another at the end - which contents do you want to see? You have no idea when (or even if) this function is going to get invoked, because Haskell sees this function as pure and feels free to reorder the execution of any or all pure functions as needed.<br />
# Attempts to read the contents of files with the same name can be factored (''i.e.'' reduced to a single call) despite the fact that the file (or the current directory) can be changed between calls. Again, Haskell considers all non-IO functions to be pure and feels free to omit multiple calls with the same parameters.<br />
<br />
So, implementing pure functions that interact with the Real World is<br />
considered to be Bad Behavior. Good boys and girls never do it ;)<br />
<br />
<br />
Nevertheless, there are (semi-official) ways to use IO actions inside<br />
of pure functions. As you should remember this is prohibited by<br />
requiring the RealWorld "baton" in order to call an IO action. Pure functions don't have the baton, but there is a special "magic" procedure that produces this baton from nowhere, uses it to call an IO action and then throws the resulting "world" away! It's a little low-level magic :) This very special (and dangerous) procedure is:<br />
<br />
<haskell><br />
unsafePerformIO :: IO a -> a<br />
</haskell><br />
<br />
Let's look at its (possible) definition:<br />
<br />
<haskell><br />
unsafePerformIO :: (RealWorld -> (a, RealWorld)) -> a<br />
unsafePerformIO action = let (a, world1) = action createNewWorld<br />
in a<br />
</haskell><br />
<br />
where 'createNewWorld' is an internal function producing a new value of<br />
the RealWorld type.<br />
<br />
Using unsafePerformIO, you can easily write pure functions that do<br />
I/O inside. But don't do this without a real need, and remember to<br />
follow this rule: the compiler doesn't know that you are cheating; it still<br />
considers each non-IO function to be a pure one. Therefore, all the usual<br />
optimization rules can (and will!) be applied to its execution. So<br />
you must ensure that:<br />
<br />
# The result of each call depends only on its arguments.<br />
# You don't rely on side-effects of this function, which may be not executed if its results are not needed.<br />
<br />
<br />
Let's investigate this problem more deeply. Function evaluation in Haskell<br />
is determined by a value's necessity - the language computes only the values that are really required to calculate the final result. But what does this mean with respect to the 'main' function? To "calculate the final world's" value, you need to perform all the intermediate IO actions that are included in the 'main' chain. By using 'unsafePerformIO' we call IO actions outside of this chain. What guarantee do we have that they will be run at all? None. The only time they will be run is if running them is required to compute the overall function result (which in turn should be required to perform some action in the<br />
'main' chain). This is an example of Haskell's evaluation-by-need strategy. Now you should clearly see the difference:<br />
<br />
- An IO action inside an IO procedure is guaranteed to execute as long as<br />
it is (directly or indirectly) inside the 'main' chain - even when its result isn't used (because the implicit "world" value it returns ''will'' be used). You directly specify the order of the action's execution inside the IO procedure. Data dependencies are simulated via the implicit "world" values that are passed from each IO action to the next.<br />
<br />
- An IO action inside 'unsafePerformIO' will be performed only if<br />
result of this operation is really used. The evaluation order is not<br />
guaranteed and you should not rely on it (except when you're sure about<br />
whatever data dependencies may exist).<br />
<br />
<br />
I should also say that inside 'unsafePerformIO' call you can organize<br />
a small internal chain of IO actions with the help of the same binding<br />
operators and/or 'do' syntactic sugar we've seen above. For example, here's a particularly convoluted way to compute the integer that comes after zero:<br />
<br />
<haskell><br />
one :: Int<br />
one = unsafePerformIO $ do var <- newIORef 0<br />
writeIORef var 1<br />
readIORef var<br />
</haskell><br />
<br />
and in this case ALL the operations in this chain will be performed as<br />
long as the result of the 'unsafePerformIO' call is needed. To ensure this,<br />
the actual 'unsafePerformIO' implementation evaluates the "world" returned<br />
by the 'action':<br />
<br />
<haskell><br />
unsafePerformIO action = let (a,world1) = action createNewWorld<br />
in (world1 `seq` a)<br />
</haskell><br />
<br />
(The 'seq' operation strictly evaluates its first argument before<br />
returning the value of the second one).<br />
<br />
<br />
<br />
But there is an even stranger operation called 'unsafeInterleaveIO' that<br />
gets the "official baton", makes its own pirate copy, and then runs<br />
an "illegal" relay-race in parallel with the main one! I can't talk further<br />
about its behavior without causing grief and indignation, so it's no surprise<br />
that this operation is widely used in countries that are hotbeds of software piracy such as Russia and China! ;) Don't even ask me - I won't say anything more about this dirty trick I use all the time ;)<br />
<br />
<br />
<br />
== Welcome to the machine: the actual [[GHC]] implementation ==<br />
<br />
A little disclaimer: I should say that I'm not describing<br />
here exactly what a monad is (I don't even completely understand it myself) and my explanation shows only one _possible_ way to implement the IO monad in<br />
Haskell. For example, the hbc Haskell compiler implements monads via<br />
continuations. I also haven't said anything about exception handling,<br />
which is a natural part of the "monad" concept. You can read the "All About<br />
Monads" guide to learn more about these topics.<br />
<br />
But there is some good news: first, the monad understanding you've just acquired will work with any implementation. You just can't work with RealWorld<br />
values directly.<br />
<br />
Second, the IO monad implementation described here is really used in the GHC,<br />
yhc/nhc (Hugs/jhc, too?) compilers. Here is the actual IO definition<br />
from the GHC sources:<br />
<br />
<haskell><br />
newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))<br />
</haskell><br />
<br />
It uses the "State# RealWorld" type instead of our RealWorld, it uses the "(# #)" strict tuple for optimization, and it adds an IO data constructor<br />
around the type. Nevertheless, there are no significant changes from the standpoint of our explanation. Knowing the principle of "chaining" IO actions via fake "state of the world" values, you can now easily understand and write low-level implementations of GHC I/O operations.<br />
<br />
<br />
=== The [[Yhc]]/nhc98 implementation ===<br />
<br />
<haskell><br />
data World = World<br />
newtype IO a = IO (World -> Either IOError a)<br />
</haskell><br />
<br />
This implementation makes the "World" disappear somewhat, and returns Either a<br />
result of type "a", or if an error occurs then "IOError". The lack of the World on the right-hand side of the function can only be done because the compiler knows special things about the IO type, and won't overoptimise it.<br />
<br />
<br />
== Further reading ==<br />
<br />
This tutorial is largely based on the Simon Peyton Jones' paper [http://research.microsoft.com/%7Esimonpj/Papers/marktoberdorf Tackling the awkward squad: monadic input/output, concurrency, exceptions, and foreign-language calls in Haskell]. I hope that my tutorial improves his original explanation of the Haskell I/O system and brings it closer to the point of view of beginning Haskell programmers. But if you need to learn about concurrency, exceptions and FFI in Haskell/GHC, the original paper is the best source of information.<br />
<br />
You can find more information about concurrency, FFI and STM at the [[GHC/Concurrency#Starting points]] page.<br />
<br />
The [[Arrays]] page contains exhaustive explanations about using mutable arrays.<br />
<br />
Look also at the [[Books and tutorials#Using Monads]] page, which contains tutorials and papers really describing these mysterious monads :)<br />
<br />
An explanation of the basic monad functions, with examples, can be found in the reference guide [http://members.chello.nl/hjgtuyl/tourdemonad.html A tour of the Haskell Monad functions], by Henk-Jan van Tuyl.<br />
<br />
Do you have more questions? Ask in the [http://www.haskell.org/mailman/listinfo/haskell-cafe haskell-cafe mailing list].<br />
<br />
<br />
== To-do list ==<br />
<br />
If you are interested in adding more information to this manual, please add your questions/topics here.<br />
<br />
Topics:<br />
* fixIO and 'mdo'<br />
* ST monad<br />
* Q monad<br />
<br />
Questions:<br />
* split '>>='/'>>'/return section and 'do' section, more examples of using binding operators<br />
* IORef detailed explanation (==const*), usage examples, syntax sugar, unboxed refs<br />
* control structures developing - much more examples<br />
* unsafePerformIO usage examples: global variable, ByteString, other examples<br />
* actual GHC implementation - how to write low-level routines on example of newIORef implementation<br />
<br />
This manual is collective work, so feel free to add more information to it yourself. The final goal is to collectively develop a comprehensive manual for using the IO monad.<br />
<br />
----<br />
<br />
[[Category:Tutorials]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/ArrayRef&diff=4681Library/ArrayRef2006-07-09T18:24:36Z<p>HenkJanVanTuyl: </p>
<hr />
<div>The Arrays&References library supports Hugs 2003-2006 and GHC 6.4.<br />
It includes the following features:<br />
<br />
==Unboxed references==<br />
<br />
This substitutes the numerous "fast mutable Ints", "fast mutable<br />
Bools" and "fast mutable Ptrs" ghc-specific modules that are used in<br />
almost any large project. In contrast to them, this library mimics<br />
the well-known interface of IORef/STRef:<br />
<br />
<haskell><br />
import Data.Ref<br />
main = do x <- newIOURef (0::Int)<br />
writeIOURef x 1<br />
a <- readIOURef x<br />
print a<br />
</haskell><br />
<br />
Unboxed references for the IO monad have the type "IOURef a" and operations<br />
newIOURef, readIOURef, writeIOURef. Unboxed references for the ST monad<br />
have the type "STURef s a" and operations newSTURef, readSTURef,<br />
writeSTURef.<br />
<br />
Unboxed references can only contain values of the following types:<br />
Bool, Char, Int, Int8..Int64, Word, Word8..Word64, Float, Double,<br />
Ptr a, FunPtr a, StablePtr a. These types are members of Unboxed class<br />
and you can implement new instances of this class by converting values<br />
of some other type (say, CChar) to values of an already supported type.<br />
<br />
Despite all these improvements, operations with unboxed references are<br />
compiled to the same code as for any "fast mutable variables". Moreover,<br />
unboxed references are available even for Hugs, which allows simplified<br />
debugging of programs that use them. Please note that unboxed references<br />
always hold computed values, in contrast to boxed references, which can<br />
contain unevaluated thunks.<br />
<br />
I wish to thank Simon Marlow and especially Oleg Kiselyov who proposed<br />
the idea of these references and their implementation (in particular, see<br />
http://www.haskell.org/pipermail/haskell-cafe/2006-February/014324.html)<br />
<br />
You can find examples of using unboxed references in "Examples/URef.hs"<br />
<br />
<br />
<br />
==Monad-independent references==<br />
<br />
Sometimes you need to write code that will be compatible with both IO<br />
and ST monads, and even better with any monad that is lifted from<br />
one of these two. This is especially useful for writing library code that<br />
should be as generic as possible. Operations for arrays, for example,<br />
are ready for such a kind of usage - readArray and writeArray can work<br />
in any monad. But this is not true for references - you need to use<br />
readIORef for the IO monad, but readSTRef for the ST monad, so if you need to<br />
implement a monad-independent algorithm that uses references, you will<br />
be in trouble. This module solves this problem by providing<br />
monad-independent operations on boxed and unboxed references. So, the<br />
following routine:<br />
<br />
<haskell><br />
test_Ref = do x <- newRef (0::Int)<br />
writeRef x 1<br />
readRef x<br />
</haskell><br />
<br />
can be executed in both the IO and the ST monads:<br />
<br />
<haskell><br />
main = do a <- test_Ref<br />
print a<br />
let b = runST test_Ref<br />
print b<br />
</haskell><br />
<br />
This example uses the boxed references; unboxed references can be used<br />
in a similar way with operations newURef, readURef, writeURef.<br />
<br />
You can find examples of writing monad-independent routines in<br />
"Examples/Universal.hs". Another library of mine, [[Library/Streams]], widely uses this<br />
facility to implement common functionality for streams working in<br />
different monads.<br />
<br />
<br />
<br />
==Syntax sugar for mutable types==<br />
<br />
Haskell doesn't support a convenient syntax for using mutable vars, such<br />
as references, arrays and hash tables. The library includes a module<br />
that partially simplifies their usage. For example:<br />
<br />
<haskell><br />
main = do -- syntax sugar used for reference:<br />
x <- ref (0::Int)<br />
x += 1<br />
x .= (*2)<br />
a <- val x<br />
print a<br />
<br />
-- syntax sugar used for array:<br />
arr <- newArray (0,9) 0 :: IO Array Int Int<br />
(arr,0) =: 1<br />
b <- val (arr,0)<br />
print b<br />
</haskell><br />
<br />
Basically, the module supports syntactic sugar for using the following<br />
data types: all types of references, arrays and hash tables. Also, it<br />
includes two operations to creating references - ref (=newRef) and<br />
uref (=newURef). Other operations include<br />
<br />
=: assign<br />
+= increase<br />
-= decrease<br />
.= apply a pure function to the contents<br />
.<- apply a monadic computation to the contents<br />
val return current value<br />
<br />
The left part of these operations can be a reference, array or hash element. Code examples:<br />
<br />
reference x += 1<br />
(array,index) (arr,0) =: 1<br />
(hash,key) (hash,"str") .= (*2)<br />
<br />
You can also omit extra parentheses when indexing a two- or three-dimensional array:<br />
(arr,0,1) =: 1<br />
is equivalent to<br />
(arr,(0,1)) =: 1<br />
<br />
Note, that this module supports the array implementations<br />
included in this library, not the standard Data.Array.* modules. Module<br />
"Examples/SyntaxSugar.hs" contains further examples.<br />
<br />
<br />
<br />
==Reimplemented Arrays library==<br />
<br />
The library also includes modified implementations of Data.Array.*<br />
modules. The main benefit of these modifications is a simplified internal<br />
library structure<br />
<br />
Nevertheless, it also includes a few user-visible changes:<br />
<br />
- Unboxed arrays now can be used in polymorphic functions, they are defined<br />
for every element type that belongs to the classes Unboxed and HasDefaultValue<br />
(again, look at http://www.haskell.org/pipermail/haskell-cafe/2004-July/006400.html).<br />
You can add new instances to these classes<br />
<br />
- The MArray class now supports arrays with dynamic bounds. It includes<br />
monadic operation getBounds, and if you change your code to use<br />
this operation with mutable arrays instead of `bounds`, your code will also<br />
be ready to work with dynamic (resizable) arrays<br />
<br />
- Support for dynamic (resizable) arrays is included. Their bounds can be<br />
changed either explicitly (by `resizeDynamicArray`) or implicitly (by<br />
writing to non-existing position). Policy of automatic array expansion<br />
is selected (or disabled) on array creation.<br />
<br />
- Unboxed arrays of Bool values occupy one byte per element (in the old<br />
implementation they used one bit per element)<br />
<br />
- castUArray/castIOUArray/castSTUArray operations are non-monadic,<br />
require "Enum ix" and recalculate upper bounds of arrays according to the<br />
size of their elements: UArray (1,2) Word32 -> UArray (1,8) Word8<br />
<br />
- Some operations may be slower in the new implementation, because I'm<br />
not sure that I discovered all the clever tricks used in the original library.<br />
Please test speed and report me about any problems<br />
<br />
In other aspects, the new arrays are equivalent to the old ones.<br />
Just change "Array" to "ArrayBZ" in your import statements and<br />
enjoy! :) Directory "Examples/Array" contains demonstrations of using<br />
each array type<br />
<br />
<br />
===Changes in MArray usage===<br />
<br />
The old Arrays library contained the following definitions:<br />
<br />
<haskell><br />
class HasBounds a where<br />
bounds :: Ix i => a i e -> (i,i)<br />
class (Monad m, HasBounds a) => MArray a e m where<br />
...<br />
</haskell><br />
<br />
In the new library, the MArray class is defined as:<br />
<br />
<haskell><br />
class (Monad m) => HasMutableBounds a m where<br />
getBounds :: Ix i => a i e -> m (i,i)<br />
class (Monad m, HasMutableBounds a m) => MArray a e m where<br />
...<br />
</haskell><br />
<br />
This means that definitions like this will no longer work:<br />
<br />
<haskell><br />
arrayHead :: (MArray a e m, Ix i) => a i e -> m e<br />
arrayHead marr = case bounds marr of<br />
(l,_) -> readArray marr l<br />
</haskell><br />
<br />
because the `bounds` operation is part of HasBounds class, that is no longer a<br />
base class for MArray. What can you do to fix this problem? Either:<br />
<br />
- Add a HasBounds restriction to the operation type:<br />
<br />
<haskell><br />
arrayHead :: (MArray a e m, HasBounds a, Ix i) => a i e -> m e<br />
</haskell><br />
<br />
This way, your code will become compatible with both the old and the new<br />
versions of Arrays library, but it will work only with "old" mutable<br />
arrays and won't support dynamic arrays.<br />
<br />
- Replace calls to the `bounds` operation with calls to `getBounds`. This<br />
way, your function will become compatible with any instance of the MArray<br />
class, including dynamic arrays:<br />
<br />
<haskell><br />
arrayHead marr = do (l,_) <- getBounds marr<br />
readArray marr l<br />
</haskell><br />
<br />
I should mention that, despite the fact that MArray isn't based on the HasBounds<br />
class anymore, all the old mutable array types (IOArray..StorableArray) still<br />
implement this interface. Only the new dynamic arrays don't implement<br />
it because this is impossible. So, you can use the `bounds` operation<br />
in code that works with one of "old" array constructors:<br />
<br />
<haskell><br />
arrayHead :: IOArray i e -> IO e<br />
arrayHead marr = case bounds marr of<br />
(l,_) -> readArray marr l<br />
</haskell><br />
<br />
<br />
===Using dynamic (resizable) arrays===<br />
<br />
Just to let you know - the current implementation of dynamic arrays is<br />
very trivial: it just saves a reference (IORef or STRef) to the mutable<br />
array. When a dynamic array is resized, a new mutable array is allocated and the<br />
contents is copied. New elements are filled with the same default value as when<br />
the array was created with the newArray<br />
or newDynamicArray operation. If a dynamic array is created with<br />
newArray_ or newDynamicArray_, then new elements will be left<br />
undefined.<br />
<br />
A dynamic array can be resized explicitly by the resizeDynamicArray operation:<br />
<br />
<haskell><br />
resizeDynamicArray array (l,u)<br />
</haskell><br />
<br />
where (l,u) are new array bounds. If the dynamic array was created<br />
by a newArray or newArray_ operation, it is the only way to resize it -<br />
attempts to write beyond current bounds will raise an exception:<br />
<br />
<haskell><br />
arr <- newArray (0,-1) 99 :: IO (DynamicIOArray Int Int)<br />
resizeDynamicArray arr (0,0)<br />
writeArray arr 1 1 -- this operation raises an exception<br />
</haskell><br />
<br />
<br />
To create an array that will be automatically resized on attempt to write<br />
beyond current bounds, you should use a newDynamicArray or<br />
newDynamicArray_ operation (the former initializes an array with a given value,<br />
while the latter leaves the array uninitialized). Their first argument<br />
determines the array expansion policy:<br />
<br />
<haskell><br />
arr <- newDynamicArray_ growTwoTimes (0,-1) :: IO (DynamicIOArray Int Int)<br />
</haskell><br />
<br />
This array will grow to at least two times its current size, each time automatic<br />
expansion occurs, which is determined by using the `growTwoTimes`<br />
parameter. This parameter is just an ordinary function that has the<br />
following type:<br />
<br />
<haskell><br />
type GrowBoundsF i = (i,i) -> i -> (i,i)<br />
</haskell><br />
<br />
This function accepts old array bounds and offending index and<br />
returns new array bounds. You can write new functions for expansion<br />
policies yourself, or use one of predefined ones:<br />
<br />
growTwoTimes - expand array to at least two times its current size<br />
growMinimally - minimal growth that ensures inclusion of new index<br />
noGrow - disable automatic growth. This policy is used for arrays created by newArray or newArray_<br />
<br />
Please note that not every array can work with every expansion policy<br />
and that is why I supported freedom of selection of this policy. Only<br />
the noGrow policy is compatible with every index type. The growMinimally<br />
policy by its type is compatible with any index, but it will not work<br />
for partially ordered indexes, in particular for multi-dimensional<br />
arrays. Imagine, for example, an array with the bounds (0,0)..(9,9). When you<br />
try to write to index (15,5), this expansion policy function will<br />
be unable to determine what the new bounds should be (0,0)..(15,9). So you<br />
should always provide a custom expansion policy function for partially<br />
ordered indexes. At last, the growTwoTimes policy is compatible only with<br />
indexes belonging to class Num, but it is the most useful policy of all,<br />
because it ensures that the program will not spend all its<br />
time expanding arrays. On the other hand, you can provide your own<br />
policy function that will, for example, expand an array only 1.5 times.<br />
<br />
Dynamic arrays support the same MArray and HasMutableBounds interfaces<br />
as other mutable arrays, but they don't support the HasBounds interface.<br />
<br />
<br />
And now about types of dynamic arrays. These types reflect all the<br />
types you can use for mutable arrays, and include DynamicIOArray,<br />
DynamicIOUArray, DynamicSTArray, DynamicSTUArray, which have the<br />
same parameters as corresponding arrays without the "Dynamic" prefix.<br />
Some examples are:<br />
<br />
<haskell><br />
DynamicIOArray Int Double<br />
DynamicSTUArray s (Int,Int) Bool<br />
</haskell><br />
<br />
You can also create dynamic arrays from other mutable array types<br />
working in the IO monad:<br />
<br />
<haskell><br />
DynamicIO StorableArray Int Double<br />
</haskell><br />
<br />
or the ST monad:<br />
<br />
<haskell><br />
DynamicST s (STUArray s) (Int,Int) Bool<br />
</haskell><br />
<br />
or any other monad (ask me if you need this). Btw, implementation of<br />
dynamic arrays use the monad-independent references class mentioned<br />
above.<br />
<br />
<br />
See "Examples/Array/Dynamic.hs" for further examples on using these arrays.<br />
<br />
<br />
== Downloading and installation ==<br />
<br />
You can download the latest version of the library at http://www.haskell.org/library/ArrayRef.tar.gz<br />
<br />
The library is cabalized. To install it, run command:<br />
<br />
make install<br />
<br />
Directory "Examples" contains usage examples for the library.<br />
<br />
<br />
This wiki page is official library documentation.<br />
Please continue to improve it and add more information about using the library.<br />
Feel free to ask me about library usage via email:<br />
[mailto:Bulat.Ziganshin@gmail.com Bulat.Ziganshin@gmail.com]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/ArrayRef&diff=4680Library/ArrayRef2006-07-09T17:57:46Z<p>HenkJanVanTuyl: </p>
<hr />
<div>The Arrays&References library supports Hugs 2003-2006 and GHC 6.4.<br />
It includes the following features:<br />
<br />
==Unboxed references==<br />
<br />
This substitutes the numerous "fast mutable Ints", "fast mutable<br />
Bools" and "fast mutable Ptrs" ghc-specific modules that are used in<br />
almost any large project. In contrast to them, this library mimics<br />
the well-known interface of IORef/STRef:<br />
<br />
<haskell><br />
import Data.Ref<br />
main = do x <- newIOURef (0::Int)<br />
writeIOURef x 1<br />
a <- readIOURef x<br />
print a<br />
</haskell><br />
<br />
Unboxed references for the IO monad have the type "IOURef a" and operations<br />
newIOURef, readIOURef, writeIOURef. Unboxed references for the ST monad<br />
have the type "STURef s a" and operations newSTURef, readSTURef,<br />
writeSTURef.<br />
<br />
Unboxed references can only contain values of the following types:<br />
Bool, Char, Int, Int8..Int64, Word, Word8..Word64, Float, Double,<br />
Ptr a, FunPtr a, StablePtr a. These types are members of Unboxed class<br />
and you can implement new instances of this class by converting values<br />
of some other type (say, CChar) to values of an already supported type.<br />
<br />
Despite all these improvements, operations with unboxed references are<br />
compiled to the same code as for any "fast mutable variables". Moreover,<br />
unboxed references are available even for Hugs which allows simplified<br />
debugging of programs that use them. Please note that unboxed references<br />
always hold computed values, in contrast to boxed references, which can<br />
contain unevaluated thunks.<br />
<br />
I wish to thank Simon Marlow and especially Oleg Kiselyov who proposed<br />
the idea of these references and their implementation (in particular, see<br />
http://www.haskell.org/pipermail/haskell-cafe/2006-February/014324.html)<br />
<br />
You can find examples of using unboxed references in "Examples/URef.hs"<br />
<br />
<br />
<br />
==Monad-independent references==<br />
<br />
Sometimes you need to write code that will be compatible with both IO<br />
and ST monads, and even better with any monad that is lifted from<br />
one of these two. This is especially useful for writing library code that<br />
should be as generic as possible. Operations for arrays, for example,<br />
are ready for such a kind of usage - readArray and writeArray can work<br />
in any monad. But this is not true for references - you need to use<br />
readIORef for the IO monad, but readSTRef for the ST monad, so if you need to<br />
implement a monad-independent algorithm that uses references, you will<br />
be in trouble. This module solves this problem by providing<br />
monad-independent operations on boxed and unboxed references. So, the<br />
following routine:<br />
<br />
<haskell><br />
test_Ref = do x <- newRef (0::Int)<br />
writeRef x 1<br />
readRef x<br />
</haskell><br />
<br />
can be executed in both the IO and the ST monads:<br />
<br />
<haskell><br />
main = do a <- test_Ref<br />
print a<br />
let b = runST test_Ref<br />
print b<br />
</haskell><br />
<br />
This example uses the boxed references; unboxed references can be used<br />
in a similar way with operations newURef, readURef, writeURef.<br />
<br />
You can find examples of writing monad-independent routines in<br />
"Examples/Universal.hs". Another library of mine, [[Library/Streams]], widely uses this<br />
facility to implement common functionality for streams working in<br />
different monads.<br />
<br />
<br />
<br />
==Syntax sugar for mutable types==<br />
<br />
Haskell doesn't support a convenient syntax for using mutable vars, such<br />
as references, arrays and hash tables. The library includes a module<br />
that partially simplifies their usage. For example:<br />
<br />
<haskell><br />
main = do -- syntax sugar used for reference:<br />
x <- ref (0::Int)<br />
x += 1<br />
x .= (*2)<br />
a <- val x<br />
print a<br />
<br />
-- syntax sugar used for array:<br />
arr <- newArray (0,9) 0 :: IO Array Int Int<br />
(arr,0) =: 1<br />
b <- val (arr,0)<br />
print b<br />
</haskell><br />
<br />
Basically, the module supports syntactic sugar for using the following<br />
data types: all types of references, arrays and hash tables. Also, it<br />
includes two operations to creating references - ref (=newRef) and<br />
uref (=newURef). Other operations include<br />
<br />
=: assign<br />
+= increase<br />
-= decrease<br />
.= apply a pure function to the contents<br />
.<- apply a monadic computation to the contents<br />
val return current value<br />
<br />
The left part of these operations can be a reference, array or hash element. Code examples:<br />
<br />
reference x += 1<br />
(array,index) (arr,0) =: 1<br />
(hash,key) (hash,"str") .= (*2)<br />
<br />
You can also omit extra parentheses when indexing a two- or three-dimensional array:<br />
(arr,0,1) =: 1<br />
is equivalent to<br />
(arr,(0,1)) =: 1<br />
<br />
Note, that this module supports the array implementations<br />
included in this library, not the standard Data.Array.* modules. Module<br />
"Examples/SyntaxSugar.hs" contains further examples.<br />
<br />
<br />
<br />
==Reimplemented Arrays library==<br />
<br />
The library also includes modified implementations of Data.Array.*<br />
modules. The main benefit of these modifications is a simplified internal<br />
library structure<br />
<br />
Nevertheless, it also includes a few user-visible changes:<br />
<br />
- Unboxed arrays now can be used in polymorphic functions, they are defined<br />
for every element type that belongs to the classes Unboxed and HasDefaultValue<br />
(again, look at http://www.haskell.org/pipermail/haskell-cafe/2004-July/006400.html).<br />
You can add new instances to these classes<br />
<br />
- The MArray class now supports arrays with dynamic bounds. It includes<br />
monadic operation getBounds, and if you change your code to use<br />
this operation with mutable arrays instead of `bounds`, your code will also<br />
be ready to work with dynamic (resizable) arrays<br />
<br />
- Support for dynamic (resizable) arrays is included. Their bounds can be<br />
changed either explicitly (by `resizeDynamicArray`) or implicitly (by<br />
writing to non-existing position). Policy of automatic array expansion<br />
is selected (or disabled) on array creation.<br />
<br />
- Unboxed arrays of Bool values occupy one byte per element (in the old<br />
implementation they used one bit per element)<br />
<br />
- castUArray/castIOUArray/castSTUArray operations are non-monadic,<br />
require "Enum ix" and recalculate upper bounds of arrays according to the<br />
size of their elements: UArray (1,2) Word32 -> UArray (1,8) Word8<br />
<br />
- Some operations may be slower in the new implementation, because I'm<br />
not sure that I discovered all the clever tricks used in the original library.<br />
Please test speed and report me about any problems<br />
<br />
In other aspects, the new arrays are equivalent to the old ones.<br />
Just change "Array" to "ArrayBZ" in your import statements and<br />
enjoy! :) Directory "Examples/Array" contains demonstrations of using<br />
each array type<br />
<br />
<br />
===Changes in MArray usage===<br />
<br />
The old Arrays library contained the following definitions:<br />
<br />
<haskell><br />
class HasBounds a where<br />
bounds :: Ix i => a i e -> (i,i)<br />
class (Monad m, HasBounds a) => MArray a e m where<br />
...<br />
</haskell><br />
<br />
In the new library, the MArray class is defined as:<br />
<br />
<haskell><br />
class (Monad m) => HasMutableBounds a m where<br />
getBounds :: Ix i => a i e -> m (i,i)<br />
class (Monad m, HasMutableBounds a m) => MArray a e m where<br />
...<br />
</haskell><br />
<br />
This means that definitions like this will no longer work:<br />
<br />
<haskell><br />
arrayHead :: (MArray a e m, Ix i) => a i e -> m e<br />
arrayHead marr = case bounds marr of<br />
(l,_) -> readArray marr l<br />
</haskell><br />
<br />
because the `bounds` operation is part of HasBounds class, that is no longer a<br />
base class for MArray. What can you do to fix this problem? Either:<br />
<br />
- Add a HasBounds restriction to the operation type:<br />
<br />
<haskell><br />
arrayHead :: (MArray a e m, HasBounds a, Ix i) => a i e -> m e<br />
</haskell><br />
<br />
This way, your code will become compatible with both the old and the new<br />
versions of Arrays library, but it will work only with "old" mutable<br />
arrays and won't support dynamic arrays.<br />
<br />
- Replace calls to the `bounds` operation with calls to `getBounds`. This<br />
way, your function will become compatible with any instance of the MArray<br />
class, including dynamic arrays:<br />
<br />
<haskell><br />
arrayHead marr = do (l,_) <- getBounds marr<br />
readArray marr l<br />
</haskell><br />
<br />
I should mention that, despite the fact that MArray isn't based on the HasBounds<br />
class anymore, all the old mutable array types (IOArray..StorableArray) still<br />
implement this interface. Only the new dynamic arrays don't implement<br />
it because this is impossible. So, you can use the `bounds` operation<br />
in code that works with one of "old" array constructors:<br />
<br />
<haskell><br />
arrayHead :: IOArray i e -> IO e<br />
arrayHead marr = case bounds marr of<br />
(l,_) -> readArray marr l<br />
</haskell><br />
<br />
<br />
===Using dynamic (resizable) arrays===<br />
<br />
Just to let you know - the current implementation of dynamic arrays is<br />
very trivial: it just saves a reference (IORef or STRef) to the mutable<br />
array. When a dynamic array is resized, a new mutable array is allocated and the<br />
contents is copied. New elements are filled with the same default value as when<br />
the array was created with the newArray<br />
or newDynamicArray operation. If a dynamic array is created with<br />
newArray_ or newDynamicArray_, then new elements will be left<br />
undefined.<br />
<br />
A dynamic array can be resized explicitly by the resizeDynamicArray operation:<br />
<br />
<haskell><br />
resizeDynamicArray array (l,u)<br />
</haskell><br />
<br />
where (l,u) are new array bounds. If the dynamic array was created<br />
by a newArray or newArray_ operation, it is the only way to resize it -<br />
attempts to write beyond current bounds will raise an exception:<br />
<br />
<haskell><br />
arr <- newArray (0,-1) 99 :: IO (DynamicIOArray Int Int)<br />
resizeDynamicArray arr (0,0)<br />
writeArray arr 1 1 -- this operation raises an exception<br />
</haskell><br />
<br />
<br />
To create an array that will be automatically resized on attempt to write<br />
beyond current bounds, you should use a newDynamicArray or<br />
newDynamicArray_ operation (the former initializes an array with a given value,<br />
while the latter leaves the array uninitialized). Their first argument<br />
determines the array expansion policy:<br />
<br />
<haskell><br />
arr <- newDynamicArray_ growTwoTimes (0,-1) :: IO (DynamicIOArray Int Int)<br />
</haskell><br />
<br />
This array will grow to at least two times its current size, each time automatic<br />
expansion occurs, which is determined by using the `growTwoTimes`<br />
parameter. This parameter is just an ordinary function that has the<br />
following type:<br />
<br />
<haskell><br />
type GrowBoundsF i = (i,i) -> i -> (i,i)<br />
</haskell><br />
<br />
This function accepts old array bounds and offending index and<br />
returns new array bounds. You can write new functions for expansion<br />
policies yourself, or use one of predefined ones:<br />
<br />
growTwoTimes - expand array to at least two times its current size<br />
growMinimally - minimal growth that ensures inclusion of new index<br />
noGrow - disable automatic growth. This policy is used for arrays created by newArray or newArray_<br />
<br />
Please note that not every array can work with every expansion policy<br />
and that is why I supported freedom of selection of this policy. Only<br />
the noGrow policy is compatible with every index type. The growMinimally<br />
policy by it's type is compatible with any index, but it will not work<br />
for partially ordered indexes, in particular for multi-dimensional<br />
arrays. Imagine, for example, an array with the bounds (0,0)..(9,9). When you<br />
try to write to index (15,5), this expansion policy function will<br />
be unable to determine what the new bounds should be (0,0)..(15,9). So you<br />
should always provide a custom expansion policy function for partially<br />
ordered indexes. At last, the growTwoTimes policy is compatible only with<br />
indexes belonging to class Num, but it is the most useful policy of all,<br />
because it ensures that the program will not spend all it's<br />
time expanding arrays. On the other hand, you can provide your own<br />
policy function that will, for example, expand an array only 1.5 times.<br />
<br />
Dynamic arrays support the same MArray and HasMutableBounds interfaces<br />
as other mutable arrays, but they don't support the HasBounds interface.<br />
<br />
<br />
And now about types of dynamic arrays. These types reflect all the<br />
types you can use for mutable arrays, and include DynamicIOArray,<br />
DynamicIOUArray, DynamicSTArray, DynamicSTUArray, which have the<br />
same parameters as corresponding arrays without the "Dynamic" prefix.<br />
Some examples are:<br />
<br />
<haskell><br />
DynamicIOArray Int Double<br />
DynamicSTUArray s (Int,Int) Bool<br />
</haskell><br />
<br />
You can also create dynamic arrays from other mutable array types<br />
working in IO monad:<br />
<br />
<haskell><br />
DynamicIO StorableArray Int Double<br />
</haskell><br />
<br />
or ST monad:<br />
<br />
<haskell><br />
DynamicST s (STUArray s) (Int,Int) Bool<br />
</haskell><br />
<br />
or any other monad (ask me if you need this). Btw, implementation of<br />
dynamic arrays use the monad-independent references class mentioned<br />
above.<br />
<br />
<br />
See "Examples/Array/Dynamic.hs" for further examples on using these arrays.<br />
<br />
<br />
== Downloading and installation ==<br />
<br />
You can download the latest version of the library at http://www.haskell.org/library/ArrayRef.tar.gz<br />
<br />
The library is cabalized. To install it, run command:<br />
<br />
make install<br />
<br />
Directory "Examples" contains usage examples for the library.<br />
<br />
<br />
This wiki page is an official library documentation.<br />
Please continue to improve it and add more information about using the library.<br />
Feel free to ask me about library usage via email:<br />
[mailto:Bulat.Ziganshin@gmail.com Bulat.Ziganshin@gmail.com]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/ArrayRef&diff=4244Library/ArrayRef2006-06-06T21:55:36Z<p>HenkJanVanTuyl: </p>
<hr />
<div>The Arrays&References library supports Hugs 2003-2006 and GHC 6.4.<br />
It includes the following features:<br />
<br />
==Unboxed references==<br />
<br />
This substitutes the numerous "fast mutable Ints", "fast mutable<br />
Bools" and "fast mutable Ptrs" ghc-specific modules that are used in<br />
almost any large project. In contrast to them, this library mimics<br />
the well-known interface of IORef/STRef:<br />
<br />
<haskell><br />
import Data.Ref<br />
main = do x <- newIOURef (0::Int)<br />
writeIOURef x 1<br />
a <- readIOURef x<br />
print a<br />
</haskell><br />
<br />
Unboxed references for IO monad have the type "IOURef a" and operations<br />
newIOURef, readIOURef, writeIOURef. Unboxed references for ST monad<br />
have the type "STURef s a" and operations newSTURef, readSTURef,<br />
writeSTURef.<br />
<br />
Unboxed references can only contain values of following types:<br />
Bool, Char, Int, Int8..Int64, Word, Word8..Word64, Float, Double,<br />
Ptr a, FunPtr a, StablePtr a. These types are members of Unboxed class<br />
and you can implement new instances of this class by converting values<br />
of some other type (say, CChar) to values of an already supported type.<br />
<br />
Despite all these improvements, operations with unboxed references are<br />
compiled to the same code as for any "fast mutable variables". Moreover,<br />
unboxed references are available even for Hugs which allows simplified<br />
debugging of programs that use them. Please note that unboxed references<br />
always hold computed values, in contrast to boxed references, which can<br />
contain unevaluated thunks.<br />
<br />
I wish to thank Simon Marlow and especially Oleg Kiselyov who proposed<br />
the idea of these references and their implementation (in particular, see<br />
http://www.haskell.org/pipermail/haskell-cafe/2006-February/014324.html)<br />
<br />
You can find examples of using unboxed references in "Examples/URef.hs"<br />
<br />
<br />
<br />
==Monad-independent references==<br />
<br />
Sometimes you need to write code that will be compatible with both IO<br />
and ST monads, and even better with any monad that is lifted from<br />
one of these two. This is especially useful for writing library code that<br />
should be as generic as possible. Operations for arrays, for example,<br />
are ready for such a kind of usage - readArray and writeArray can work<br />
in any monad. But this is not true for references - you need to use<br />
readIORef for IO monad, but readSTRef for ST monad, so if you need to<br />
implement a monad-independent algorithm that uses references, you will<br />
be in trouble. This module solves this problem by providing<br />
monad-independent operations on boxed and unboxed references. So, the<br />
following routine:<br />
<br />
<haskell><br />
test_Ref = do x <- newRef (0::Int)<br />
writeRef x 1<br />
readRef x<br />
</haskell><br />
<br />
can be executed in both the IO and the ST monads:<br />
<br />
<haskell><br />
main = do a <- test_Ref<br />
print a<br />
let b = runST test_Ref<br />
print b<br />
</haskell><br />
<br />
This example uses the boxed references; unboxed references can be used<br />
in a similar way with operations newURef, readURef, writeURef.<br />
<br />
You can find examples of writing monad-independent routines in<br />
"Examples/Universal.hs". Another library of mine, [[Library/Streams]], widely uses this<br />
facility to implement common functionality for streams working in<br />
different monads.<br />
<br />
<br />
<br />
==Syntax sugar for mutable types==<br />
<br />
Haskell doesn't support a convenient syntax for using mutable vars, such<br />
as references, arrays and hash tables. The library includes a module<br />
that partially simplifies their usage. For example:<br />
<br />
<haskell><br />
main = do -- syntax sugar used for reference:<br />
x <- ref (0::Int)<br />
x += 1<br />
x .= (*2)<br />
a <- val x<br />
print a<br />
<br />
-- syntax sugar used for array:<br />
arr <- newArray (0,9) 0 :: IO Array Int Int<br />
(arr,0) =: 1<br />
b <- val (arr,0)<br />
print b<br />
</haskell><br />
<br />
Basically, the module supports syntactic sugar for using the following<br />
data types: all types of references, arrays and hash tables. Also, it<br />
includes two operations to creating references - ref (=newRef) and<br />
uref (=newURef). Other operations include<br />
<br />
=: assign<br />
+= increase<br />
-= decrease<br />
.= apply a pure function to the contents<br />
.<- apply a monadic computation to the contents<br />
val return current value<br />
<br />
The left part of these operations can be a reference, array or hash element. Code examples:<br />
<br />
reference x += 1<br />
(array,index) (arr,0) =: 1<br />
(hash,key) (hash,"str") .= (*2)<br />
<br />
You can also omit extra parentheses when indexing a two- or three-dimensional array:<br />
(arr,0,1) =: 1<br />
is equivalent to<br />
(arr,(0,1)) =: 1<br />
<br />
Let's pay attention that this module supports array implementations<br />
included in the library, not standard Data.Array.* modules. Module<br />
"Examples/SyntaxSugar.hs" should contain further examples.<br />
<br />
<br />
<br />
==Reimplemented Arrays library==<br />
<br />
The library also includes modified implementations of Data.Array.*<br />
modules. The main benefit of these modifications is a simplified internal<br />
library structure<br />
<br />
Nevertheless, it also includes a few user-visible changes:<br />
<br />
- Unboxed arrays now can be used in polymorphic functions, they are defined<br />
for every element type that belongs to the classes Unboxed and HasDefaultValue<br />
(again, look at http://www.haskell.org/pipermail/haskell-cafe/2004-July/006400.html).<br />
You can add new instances to these classes<br />
<br />
- MArray class now supports arrays with dynamic bounds. It includes<br />
monadic operation getBounds, and if you change your code to use<br />
this operation with mutable arrays instead of `bounds`, your code will also<br />
be ready to work with dynamic (resizable) arrays<br />
<br />
- Support for dynamic (resizable) arrays is included. Their bounds can be<br />
changed either explicitly (by `resizeDynamicArray`) or implicitly (by<br />
writing to non-existing position). Policy of automatic array expansion<br />
is selected (or disabled) on array creation.<br />
<br />
- Unboxed arrays of Bool values occupy one byte per element (in the old<br />
implementation they used one bit per element)<br />
<br />
- castUArray/castIOUArray/castSTUArray operations are non-monadic,<br />
require "Enum ix" and recalculate upper bounds of arrays according to the<br />
size of their elements: UArray (1,2) Word32 -> UArray (1,8) Word8<br />
<br />
- Some operations may be slower in the new implementation, because I'm<br />
not sure that I discovered all the clever tricks used in the original library.<br />
Please test speed and report me about any problems<br />
<br />
In other aspects, the new arrays are equivalent to the old ones.<br />
Just change "Array" to the "ArrayBZ" in your import statements and<br />
enjoy! :) Directory "Examples/Array" contains demonstrations of using<br />
each array type<br />
<br />
<br />
===Changes in MArray usage===<br />
<br />
The old Arrays library contained the following definitions:<br />
<br />
<haskell><br />
class HasBounds a where<br />
bounds :: Ix i => a i e -> (i,i)<br />
class (Monad m, HasBounds a) => MArray a e m where <br />
...<br />
</haskell><br />
<br />
In the new library, MArray class defined as:<br />
<br />
<haskell><br />
class (Monad m) => HasMutableBounds a m where<br />
getBounds :: Ix i => a i e -> m (i,i)<br />
class (Monad m, HasMutableBounds a m) => MArray a e m where <br />
...<br />
</haskell><br />
<br />
This means that definitions like this will no longer work:<br />
<br />
<haskell><br />
arrayHead :: (MArray a e m, Ix i) => a i e -> m e<br />
arrayHead marr = case bounds marr of<br />
(l,_) -> readArray marr l<br />
</haskell><br />
<br />
because the `bounds` operation is part of HasBounds class, that is no longer a<br />
base class for MArray. What can you do to fix this problem? Either:<br />
<br />
- Add a HasBounds restriction to the operation type:<br />
<br />
<haskell><br />
arrayHead :: (MArray a e m, HasBounds a, Ix i) => a i e -> m e<br />
</haskell><br />
<br />
This way, your code will become compatible with both the old and the new<br />
versions of Arrays library, but it will work only with "old" mutable<br />
arrays and won't support dynamic arrays.<br />
<br />
- Replace calls to the `bounds` operation with calls to `getBounds`. This<br />
way, your function will become compatible with any instance of the MArray<br />
class, including dynamic arrays:<br />
<br />
<haskell><br />
arrayHead marr = do (l,_) <- getBounds marr<br />
readArray marr l<br />
</haskell><br />
<br />
I should mention that despite the fact that MArray isn't based on the HasBounds<br />
class anymore, all the old mutable array types (IOArray..StorableArray) still<br />
implement this interface. Only the new dynamic arrays don't implement<br />
it because this is impossible. So, you can use the `bounds` operation<br />
in code that works with one of "old" array constructors:<br />
<br />
<haskell><br />
arrayHead :: IOArray i e -> IO e<br />
arrayHead marr = case bounds marr of<br />
(l,_) -> readArray marr l<br />
</haskell><br />
<br />
<br />
===Using dynamic (resizable) arrays===<br />
<br />
Just to let you know - the current implementation of dynamic arrays is<br />
very trivial: it just saves reference (IORef or STRef) to the mutable<br />
array. When a dynamic array is resized, a new mutable array is allocated and the <br />
contents is copied. New elements are filled with the same default value as when the array was created with the newArray<br />
or newDynamicArray operation. If a dynamic array is created with<br />
newArray_ or newDynamicArray_, then new elements will be left<br />
undefined.<br />
<br />
A dynamic array can be resized explicitly by the resizeDynamicArray operation:<br />
<br />
<haskell><br />
resizeDynamicArray array (l,u)<br />
</haskell><br />
<br />
where (l,u) are new array bounds. If the dynamic array was created<br />
by a newArray or newArray_ operation, it is the only way to resize it -<br />
attempts to write beyond current bounds will raise an exception:<br />
<br />
<haskell><br />
arr <- newArray (0,-1) 99 :: IO (DynamicIOArray Int Int)<br />
resizeDynamicArray arr (0,0)<br />
writeArray arr 1 1 -- this operation raises an exception<br />
</haskell><br />
<br />
<br />
To create an array that will be automatically resized on attempt to write<br />
beyond current bounds, you should use a newDynamicArray or<br />
newDynamicArray_ operation (the former initializes an array with a given value, while the latter leaves the array uninitialized). Their first argument<br />
determines the array expansion policy:<br />
<br />
<haskell><br />
arr <- newDynamicArray_ growTwoTimes (0,-1) :: IO (DynamicIOArray Int Int)<br />
</haskell><br />
<br />
This array will grow to at least two times its current size, each time automatic<br />
expansion occurs, which is determined by using the `growTwoTimes`<br />
parameter. This parameter is just the ordinary function that has the<br />
following type:<br />
<br />
<haskell><br />
type GrowBoundsF i = (i,i) -> i -> (i,i)<br />
</haskell><br />
<br />
This function accepts old array bounds and offending index and<br />
returns new array bounds. You can write new functions for an expansion<br />
policies yourself, or use one of premastered ones:<br />
<br />
growTwoTimes - expand array to at least two times its current size<br />
growMinimally - minimal growth that ensures inclusion of new index<br />
noGrow - disable automatic growth. This policy is used for arrays created by newArray or newArray_<br />
<br />
Please note that not every array can work with every expansion policy<br />
and that is why I supported freedom of selection of this policy. Only<br />
noGrow policy is compatible with every index type. The growMinimally<br />
policy by it's type is compatible with any index, but it will not work<br />
for partially ordered indexes, in particular for multi-dimensional<br />
arrays. Imagine, for example, an array with the bounds (0,0)..(9,9). When you<br />
try to write to index (15,5), this expansion policy function will<br />
be unable to determine what the new bounds should be (0,0)..(15,9). So you<br />
should always provide a custom expansion policy function for partially<br />
ordered indexes. At last, the growTwoTimes policy is compatible only with<br />
indexes belonging to class Num, but it is the most useful policy of all, because it ensures that the program will not spend all it's<br />
time expanding arrays. On the other side, you can provide your own<br />
policy function that will, for example, expand an array only 1.5 times.<br />
<br />
Dynamic arrays support the same MArray and HasMutableBounds interfaces<br />
as other mutable arrays, but they don't support the HasBounds interface.<br />
<br />
<br />
And now about types of dynamic arrays. These types reflect all the<br />
types you can use for mutable arrays, and include DynamicIOArray,<br />
DynamicIOUArray, DynamicSTArray, DynamicSTUArray, which have the<br />
same parameters as corresponding arrays without the "Dynamic" prefix.<br />
Some examples are:<br />
<br />
<haskell><br />
DynamicIOArray Int Double<br />
DynamicSTUArray s (Int,Int) Bool<br />
</haskell><br />
<br />
You can also create dynamic arrays from other mutable array types<br />
working in IO monad:<br />
<br />
<haskell><br />
DynamicIO StorableArray Int Double<br />
</haskell><br />
<br />
or ST monad:<br />
<br />
<haskell><br />
DynamicST s (STUArray s) (Int,Int) Bool<br />
</haskell><br />
<br />
or any other monad (ask me if you need this). Btw, implementation of<br />
dynamic arrays use the monad-independent references class mentioned<br />
above.<br />
<br />
<br />
See "Examples/Array/Dynamic.hs" for further examples on using these arrays.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/ArrayRef&diff=4243Library/ArrayRef2006-06-06T21:32:50Z<p>HenkJanVanTuyl: </p>
<hr />
<div>The Arrays&References library supports Hugs 2003-2006 and GHC 6.4.<br />
It includes the following features:<br />
<br />
==Unboxed references==<br />
<br />
This substitutes the numerous "fast mutable Ints", "fast mutable<br />
Bools" and "fast mutable Ptrs" ghc-specific modules that are used in<br />
almost any large project. In contrast to them, this library mimics<br />
the well-known interface of IORef/STRef:<br />
<br />
<haskell><br />
import Data.Ref<br />
main = do x <- newIOURef (0::Int)<br />
writeIOURef x 1<br />
a <- readIOURef x<br />
print a<br />
</haskell><br />
<br />
Unboxed references for IO monad have the type "IOURef a" and operations<br />
newIOURef, readIOURef, writeIOURef. Unboxed references for ST monad<br />
have the type "STURef s a" and operations newSTURef, readSTURef,<br />
writeSTURef.<br />
<br />
Unboxed references can only contain values of following types:<br />
Bool, Char, Int, Int8..Int64, Word, Word8..Word64, Float, Double,<br />
Ptr a, FunPtr a, StablePtr a. These types are members of Unboxed class<br />
and you can implement new instances of this class by converting values<br />
of some other type (say, CChar) to values of an already supported type.<br />
<br />
Despite all these improvements, operations with unboxed references are<br />
compiled to the same code as for any "fast mutable variables". Moreover,<br />
unboxed references are available even for Hugs which allows simplified<br />
debugging of programs that use them. Please note that unboxed references<br />
always hold computed values, in contrast to boxed references, which can<br />
contain unevaluated thunks.<br />
<br />
I wish to thank Simon Marlow and especially Oleg Kiselyov who proposed<br />
the idea of these references and their implementation (in particular, see<br />
http://www.haskell.org/pipermail/haskell-cafe/2006-February/014324.html)<br />
<br />
You can find examples of using unboxed references in "Examples/URef.hs"<br />
<br />
<br />
<br />
==Monad-independent references==<br />
<br />
Sometimes you need to write code that will be compatible both with IO<br />
and ST monads, and even better with any monad that is lifted from<br />
one of these two. This is especially useful for writing library code that<br />
should be as generic as possible. Operations for arrays, for example,<br />
are ready for such a kind of usage - readArray and writeArray can work<br />
in any monad. But it's not true for references - you need to use<br />
readIORef for IO monad, but readSTRef for ST monad, so if you need to<br />
implement a monad-independent algorithm that uses references, you will<br />
be in trouble. This module solves this problem by providing<br />
monad-independent operations on boxed and unboxed references. So, the<br />
following routine:<br />
<br />
<haskell><br />
test_Ref = do x <- newRef (0::Int)<br />
writeRef x 1<br />
readRef x<br />
</haskell><br />
<br />
can be executed both in IO and ST monads:<br />
<br />
<haskell><br />
main = do a <- test_Ref<br />
print a<br />
let b = runST test_Ref<br />
print b<br />
</haskell><br />
<br />
This example uses the boxed references, unboxed references can be used<br />
in a similar way with operations newURef, readURef, writeURef.<br />
<br />
You can find examples of writing monad-independent routines in<br />
"Examples/Universal.hs". Another library of mine, [[Library/Streams]], widely uses this<br />
facility to implement common functionality for streams working in<br />
different monads.<br />
<br />
<br />
<br />
==Syntax sugar for mutable types==<br />
<br />
Haskell doesn't support a convenient syntax for using mutable vars, such<br />
as references, arrays and hash tables. The library includes a module<br />
that partially simplifies their usage. For example:<br />
<br />
<haskell><br />
main = do -- syntax sugar used for reference:<br />
x <- ref (0::Int)<br />
x += 1<br />
x .= (*2)<br />
a <- val x<br />
print a<br />
<br />
-- syntax sugar used for array:<br />
arr <- newArray (0,9) 0 :: IO Array Int Int<br />
(arr,0) =: 1<br />
b <- val (arr,0)<br />
print b<br />
</haskell><br />
<br />
Basically, the module supports syntax sugar for using the following<br />
data types: all types of references, arrays and hash tables. Also, it<br />
includes two operations to creating references - ref (=newRef) and<br />
uref (=newURef). Other operations include<br />
<br />
=: assign<br />
+= increase<br />
-= decrease<br />
.= apply a pure function to the contents<br />
.<- apply a monadic computation to the contents<br />
val return current value<br />
<br />
The left part of these operations can be a reference, array or hash element. Code examples:<br />
<br />
reference x += 1<br />
(array,index) (arr,0) =: 1<br />
(hash,key) (hash,"str") .= (*2)<br />
<br />
You can also omit extra parentheses when indexing a two- or three-dimensional array:<br />
(arr,0,1) =: 1<br />
is equivalent to<br />
(arr,(0,1)) =: 1<br />
<br />
Let's pay attention that this module supports arrays implementation<br />
included in the library, not standard Data.Array.* modules. Module<br />
"Examples/SyntaxSugar.hs" should contain further examples.<br />
<br />
<br />
<br />
==Reimplemented Arrays library==<br />
<br />
The library also includes modified implementations of Data.Array.*<br />
modules. The main benefit of these modifications is a simplified internal<br />
library structure<br />
<br />
Nevertheless, it also includes a few user-visible changes:<br />
<br />
- Unboxed arrays now can be used in polymorphic functions, they are defined<br />
for every element type that belongs to the classes Unboxed and HasDefaultValue<br />
(again, look at http://www.haskell.org/pipermail/haskell-cafe/2004-July/006400.html).<br />
You can add new instances to these classes<br />
<br />
- MArray class now supports arrays with dynamic bounds. It includes<br />
monadic operation getBounds, and if you will change your code to use<br />
this operation with mutable arrays instead of `bounds`, your code also<br />
will be ready to work with dynamic (resizable) arrays<br />
<br />
- Support for dynamic (resizable) arrays included. Their bounds can be<br />
changed either explicitly (by `resizeDynamicArray`) or implicitly (by<br />
writing to non-existing position). Policy of automatic array expansion<br />
is selected (or disabled) on array creation.<br />
<br />
- Unboxed arrays of Bool values occupy one byte per element (in the old<br />
implementation they used one bit per element)<br />
<br />
- castUArray/castIOUArray/castSTUArray operations are non-monadic,<br />
require "Enum ix" and recalculates upper bound of array according to<br />
size of elements: UArray (1,2) Word32 -> UArray (1,8) Word8<br />
<br />
- Some operations may be slower in the new implementation, because I'm<br />
not sure that I discovered all the clever tricks used in original lib.<br />
Please test speed and report me about any problems<br />
<br />
In other aspects, using of new arrays are equivalent to the old ones.<br />
Just change "Array" to the "ArrayBZ" in your import statements and<br />
enjoy! :) Directory "Examples/Array" contains demonstrations of using<br />
each array type<br />
<br />
<br />
===Changes in MArray usage===<br />
<br />
Old Arrays library contained the following definitions:<br />
<br />
<haskell><br />
class HasBounds a where<br />
bounds :: Ix i => a i e -> (i,i)<br />
class (Monad m, HasBounds a) => MArray a e m where <br />
...<br />
</haskell><br />
<br />
In new library, MArray class defined as:<br />
<br />
<haskell><br />
class (Monad m) => HasMutableBounds a m where<br />
getBounds :: Ix i => a i e -> m (i,i)<br />
class (Monad m, HasMutableBounds a m) => MArray a e m where <br />
...<br />
</haskell><br />
<br />
This means that definitions like this will no longer work:<br />
<br />
<haskell><br />
arrayHead :: (MArray a e m, Ix i) => a i e -> m e<br />
arrayHead marr = case bounds marr of<br />
(l,_) -> readArray marr l<br />
</haskell><br />
<br />
because the `bounds` operation is part of HasBounds class that is no longer a<br />
base class for MArray. What can you do to fix this problem? Either:<br />
<br />
- Add a HasBounds restriction to the operation type:<br />
<br />
<haskell><br />
arrayHead :: (MArray a e m, HasBounds a, Ix i) => a i e -> m e<br />
</haskell><br />
<br />
This way, your code will become compatible with both the old and the new<br />
versions of Arrays library, but it will work only with "old" mutable<br />
arrays and won't support dynamic arrays.<br />
<br />
- Replace calls to the `bounds` operation with calls to `getBounds`. This<br />
way, your function will become compatible with any instance of MArray<br />
class, including dynamic arrays:<br />
<br />
<haskell><br />
arrayHead marr = do (l,_) <- getBounds marr<br />
readArray marr l<br />
</haskell><br />
<br />
I should mention that despite MArray now isn't based on the HasBounds<br />
class, all the old mutable array types (IOArray..StorableArray) still<br />
implement this interface. Only the new dynamic arrays don't implement<br />
it because this is impossible. So, you can use the `bounds` operation<br />
in code that works with one of "old" array constructors:<br />
<br />
<haskell><br />
arrayHead :: IOArray i e -> IO e<br />
arrayHead marr = case bounds marr of<br />
(l,_) -> readArray marr l<br />
</haskell><br />
<br />
<br />
===Using dynamic (resizable) arrays===<br />
<br />
Just to let you know - the current implementation of dynamic arrays is<br />
very trivial: it just saves reference (IORef or STRef) to the mutable<br />
array. When a dynamic array is resized, a new mutable array is allocated and the <br />
contents is copied. New elements are filled with the value that was<br />
supported as default if array was created with the newArray<br />
or newDynamicArray operation. If a dynamic array is created with<br />
newArray_ or newDynamicArray_, then new elements will be left<br />
undefined.<br />
<br />
A dynamic array can be resized explicitly by the resizeDynamicArray operation:<br />
<br />
<haskell><br />
resizeDynamicArray array (l,u)<br />
</haskell><br />
<br />
where (l,u) are new array bounds. If the dynamic array was created<br />
by a newArray or newArray_ operation, it is the only way to resize it -<br />
attempts to write beyond current bounds will raise an exception:<br />
<br />
<haskell><br />
arr <- newArray (0,-1) 99 :: IO (DynamicIOArray Int Int)<br />
resizeDynamicArray arr (0,0)<br />
writeArray arr 1 1 -- this operation raises an exception<br />
</haskell><br />
<br />
<br />
To create an array that will be automatically resized on attempt to write<br />
beyond current bounds, you should use newDynamicArray or<br />
newDynamicArray_ operation (the former initializes an array with a given value, while the latter leaves the array uninitialized). Their first argument<br />
determines the array expansion policy:<br />
<br />
<haskell><br />
arr <- newDynamicArray_ growTwoTimes (0,-1) :: IO (DynamicIOArray Int Int)<br />
</haskell><br />
<br />
This array will grow at least two times each time automatic<br />
expansion occurs, which is determined by using the `growTwoTimes`<br />
parameter. This parameter is just the ordinary function that has the<br />
following type:<br />
<br />
<haskell><br />
type GrowBoundsF i = (i,i) -> i -> (i,i)<br />
</haskell><br />
<br />
This function accepts old array bounds and offending index and<br />
returns new array bounds. You can write new functions for an expansion<br />
policies yourself, or use one of premastered ones:<br />
<br />
growTwoTimes - expand array at least two times<br />
growMinimally - minimal growth that ensures inclusion of new index<br />
noGrow - disable automatic growth. This policy is used for arrays created by newArray or newArray_<br />
<br />
Please note that not every array can work with every expansion policy<br />
and that is why I supported freedom of selection of this policy. Only<br />
noGrow policy is compatible with every index type. The growMinimally<br />
policy by it's type is compatible with any index, but it will not work<br />
for partially ordered indexes, in particular for multi-dimensional<br />
arrays. Imagine, for example, array with bounds (0,0)..(9,9). When you<br />
try to write to index (15,5), this expansion policy function will<br />
be unable to determine what the new bounds should be (0,0)..(15,9). So you<br />
anyway should provide a custom expansion policy function for partially<br />
ordered indexes. At last, growTwoTimes policy is compatible only with<br />
indexes belonging to class Num, but it is the most useful policy of all, because it ensures that the program will not spend all it's<br />
time expanding the array. On the other side, you can provide your own<br />
policy function that will, for example, expand an array only 1.5 times.<br />
<br />
Dynamic array supports the same MArray and HasMutableBounds interfaces<br />
as other mutable arrays, but they don't support the HasBounds interface.<br />
<br />
<br />
And now about types of dynamic arrays. These types reflect all the<br />
types you can use for mutable arrays, and include DynamicIOArray,<br />
DynamicIOUArray, DynamicSTArray, DynamicSTUArray, which have the<br />
same parameters as corresponding arrays without the "Dynamic" prefix.<br />
Some examples are:<br />
<br />
<haskell><br />
DynamicIOArray Int Double<br />
DynamicSTUArray s (Int,Int) Bool<br />
</haskell><br />
<br />
You can also create dynamic arrays from other mutable array types<br />
working in IO monad:<br />
<br />
<haskell><br />
DynamicIO StorableArray Int Double<br />
</haskell><br />
<br />
or ST monad:<br />
<br />
<haskell><br />
DynamicST s (STUArray s) (Int,Int) Bool<br />
</haskell><br />
<br />
or any other monad (ask me if you need this). Btw, implementation of<br />
dynamic arrays use the monad-independent references class mentioned<br />
above.<br />
<br />
<br />
See "Examples/Array/Dynamic.hs" for further examples on using these arrays.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/Streams&diff=4149Library/Streams2006-05-21T14:42:30Z<p>HenkJanVanTuyl: </p>
<hr />
<div>== Introduction ==<br />
<br />
=== Streams: the extensible I/O library ===<br />
<br />
I have developed a new I/O library that IMHO is so sharp that it can<br />
eventually replace the current I/O facilities based on using Handles.<br />
The main advantage of the new library is its strong modular design using<br />
typeclasses. The library consists of small independent modules, each<br />
implementing one type of stream (file, memory buffer, pipe) or one<br />
part of common stream functionality (buffering, Char encoding,<br />
locking). 3rd-party libs can easily add new stream types and new common<br />
functionality. Other benefits of the new library include support for<br />
streams functioning in any monad, Hugs and GHC compatibility, high<br />
speed and an easy migration path from the existing I/O library.<br />
<br />
The Streams library is heavily based on the HVIO module written by John<br />
Goerzen. I especially want to thank John for his clever design and<br />
implementation. Really, I just renamed HVIO to Stream and presented<br />
this as my own work. :) Further development direction was inspired<br />
by the "New I/O library" written by Simon Marlow.<br />
<br />
=== Simple Streams ===<br />
<br />
The key concept of the lib is the Stream class, whose interface mimics<br />
familiar interface for Handles, just with "h" replaced with "v" in<br />
function names:<br />
<br />
<haskell><br />
class (Monad m) => Stream m h where<br />
vPutStrLn :: h -> String -> m ()<br />
vGetContents :: h -> m String<br />
vIsEOF :: h -> m Bool<br />
vClose :: h -> m ()<br />
....................<br />
</haskell><br />
<br />
This means that you already know how to use any stream! The Stream interface<br />
currently has 8 implementations: a Handle itself, raw files, pipes,<br />
memory buffers and string buffers. Future plans include support for<br />
memory-mapped files, sockets, circular memory buffers for interprocess<br />
communication and UArray-based streams.<br />
<br />
By themselves, these Stream implementations are rather simple. Basically,<br />
to implement new Stream type, it's enough to provide vPutBuf/vGetBuf<br />
operations, or even vGetChar/vPutChar. The latter way, although <br />
inefficient, allows us to implement streams that can work in any monad.<br />
StringReader and StringBuffer streams use this to provide string-based<br />
Stream class implementations both for IO and ST monads. Yes, you can<br />
use the full power of Stream operations inside the ST monad!<br />
<br />
=== Layers of Functionality ===<br />
<br />
All additional functionality is implemented via Stream Transformers,<br />
which are just parameterized Streams, whose parameters also<br />
implement the Stream interface. This allows you to apply any number of stream<br />
transformers to the raw stream and then use the result as an ordinary<br />
Stream. For example:<br />
<br />
<haskell><br />
h <- openRawFD "test" WriteMode<br />
>>= bufferBlockStream<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
</haskell><br />
<br />
This code creates a new FD, which represents a raw file, and then adds<br />
to this Stream buffering, Char encoding and locking functionality. The<br />
result type of "h" is something like this:<br />
<br />
<haskell><br />
WithLocking (WithEncoding (BufferedBlockStream FD))<br />
</haskell><br />
<br />
The complete type, as well as all the intermediate types, implements the Stream<br />
interface. Each transformer intercepts operations corresponding to its<br />
nature, and passes the rest through. For example, the encoding transformer<br />
intercepts only vGetChar/vPutChar operations and translates them to<br />
the sequences of vGetByte/vPutByte calls of the lower-level stream.<br />
The locking transformer just wraps any operation in the locking wrapper.<br />
<br />
We can trace, for example, the execution of a "vPutBuf" operation on the<br />
above-constructed Stream. First, the locking transformer acquires a lock<br />
and then passes this call to the next level. Then the encoding transformer does<br />
nothing and passes this call to the next level. The buffering<br />
transformer flushes the current buffer and passes the call further.<br />
Finally, FD itself performs the operation after all these<br />
preparations and on the returning path, the locking transformer release<br />
its lock.<br />
<br />
As another example, the "vPutChar" call on this Stream is<br />
transformed (after locking) into several "vPutByte" calls by the<br />
encoding transformer, and these bytes go to the buffer in the<br />
buffering transformer, with or without a subsequent call to the FD's<br />
"vPutBuf".<br />
<br />
=== Modularity ===<br />
<br />
As you can see, stream transformers really are independent of each<br />
other. This allows you to use them on any stream and in any combination<br />
(but you should apply them in proper order - buffering, then Char<br />
encoding, then locking). As a result, you can apply to the stream<br />
only the transformers that you really need. If you don't use the<br />
stream in multiple threads, you don't need to apply the locking<br />
transformer. If you don't use any encodings other than Latin-1 -- or<br />
don't use text I/O at all -- you don't need an encoding transformer.<br />
Moreover, you may not even need to know anything about the UserData transformer<br />
until you actually need to use it :)<br />
<br />
Both streams and stream transformers can be implemented by 3rd-party<br />
libraries. Streams and transformers from arbitrary libraries will<br />
seamlessly work together as long as they properly implement the Stream<br />
interface. My future plans include implementation of an on-the-fly<br />
(de)compression transformer and I will be happy to see 3rd-party<br />
transformers that intercept vGetBuf/vPutBuf calls and use select(),<br />
kqueue() and other methods to overlap I/O operations.<br />
<br />
=== Speed ===<br />
<br />
A quick comment about speed: it's fast enough -- 10-50 MB/s (depending<br />
on the type of operation) on a 1GHz cpu. The Handle operations, for comparison, <br />
show speed of 1-10 mb/s on the same computer. But that don't means that each <br />
and any operation in new library is 10 times faster. Strict I/O (including <br />
vGetChar/vPutChar) is a LOT faster. I included a demonstration of this <br />
fascinating speed as "Examples/wc.hs". If you need a really high speed, <br />
don't forget to increase buffer size with "vSetBuffering".<br />
<br />
On the other side, lazy I/O (including any operations that receive or return <br />
strings) show only modest speedup. This is limited by Haskell/GHC itself and <br />
I can't do much to get around these limits. Instead, I plan to provide support <br />
for I/O using packed strings. This will allow to write I/O-intensive Haskell <br />
programs that are as fast as their C counterparts.<br />
<br />
Other sources of slowness includes using of locking transformer (if you need <br />
to do this, try use "lock" around speed-critical algorithms) and complex class <br />
structure, what may be avoided by using "forall" types (I'm not sure, Simon <br />
Marlow can enlighten this topic).<br />
<br />
The library includes benchmarking code in the file "Examples/StreamsBenchmark.hs"<br />
<br />
=== Support for GHC, Hugs and other compilers ===<br />
<br />
The library is compatible with GHC 6.4<br />
<br />
<br />
The library fully supports Hugs 2003-2006, but<br />
<br />
1) support for FD and MMFile is temporarily disabled because I don't know how <br />
to build DLLs<br />
<br />
2) Hugs 2003 doesn't include support for "instance Bits Word" and vGetBuf/vPutBuf, <br />
so you need to add these implementations manually or delete the lines that use it <br />
(look for "2003" in the sources)<br />
<br />
3) WinHugs doesn't support preprocessing, so I included the MakeHugs.cmd script <br />
to preprocess source files using cpphs<br />
<br />
<br />
Main disadvantage of the library is that it supports only Hugs and GHC<br />
because of using extensions in type classe system (namely, MPTC+FD). I think that it<br />
can be made H98-compatible at the cost of excluding support for non-IO<br />
monads. I will try to make such a stripped version for other compilers<br />
if people are interested.<br />
<br />
=== Downloading and installation ===<br />
<br />
You can download the last version of this library as http://freearc.narod.ru/Streams.tar.gz<br />
<br />
<br />
The library currently doesn't support Cabal or any other automatic installation<br />
system. It's just a plain collection of modules, so to use it, you can either<br />
copy directories "Data" and "System" to the directory where your application<br />
lies or, preferably, add to your ghc's compilation command the "-i" switch<br />
with the path to directory where you unpacked library. For example:<br />
<br />
ghc --make -i/Haskell/StreamsLib YourProgram.hs<br />
<br />
In the latter case, GHC will look, for example, for module "Data.Ref" in file<br />
/Haskell/StreamsLib/Data/Ref.hs<br />
<br />
<br />
The same applies to Hugs, but in this case, the corresponding switch is named "-P":<br />
runhugs -P/Haskell/StreamsLib YourProgram.hs<br />
For WinHugs, you can setup this path through the "Options" dialog.<br />
<br />
<br />
Directory "Examples" contains examples of using library, you should copy<br />
files from this directory to the root library directory in order to compile them.<br />
<br />
=== Stage of Development ===<br />
<br />
The library is currently at the beta stage. It contains a number of<br />
known minor problems and an unknown number of yet-to-be-discovered bugs.<br />
It is not properly documented, doesn't include QuickCheck tests, is not<br />
cabalized, and not all "h*" operations have their "v*" equivalents yet.<br />
If anyone wants to join this effort in order to help fix these oddities<br />
and prepare the lib for inclusion in the standard libraries suite, I would<br />
be really happy. :) I will also be happy (although much less ;) to see<br />
bug reports and suggestions about its interface and internal<br />
organization. It's just a first public version, so we still can change<br />
everything here!<br />
<br />
In particular, this wiki page is an official library documentation. <br />
Please continue to improve it and add more information about using the library.<br />
Feel free to ask me about library usage via email: [mailto:Bulat.Ziganshin@gmail.com Bulat.Ziganshin@gmail.com]<br />
<br />
<br />
== Overview of Stream Transformers ==<br />
<br />
=== Buffering ===<br />
<br />
There are three buffering transformers. Each buffering transformer<br />
implements support for vGetByte, vPutChar, vGetContents and other<br />
byte- and text-oriented operations for the streams, which by themselves<br />
support only vGetBuf/vPutBuf (or vReceiveBuf/vSendBuf) operations.<br />
<br />
The first transformer can be applied to any stream supporting<br />
vGetBuf/vPutBuf. This is applied by the operation "bufferBlockStream". The<br />
well-known vSetBuffering/vGetBuffering operations are intercepted by<br />
this transformer and used to control buffer size. At this moment, only<br />
BlockBuffering is implemented, while LineBuffering and NoBuffering are<br />
only in the planning stages.<br />
<br />
Two other transformers can be applied to streams that implement<br />
vReceiveBuf/vSendBuf operations -- that is, streams whose data<br />
reside in memory, including in-memory streams and memory-mapped<br />
files. In these cases, the buffering transformer doesn't need to allocate<br />
a buffer itself, it just requests from the underlying stream the address and<br />
size of the next available portion of data. Nevertheless, the final<br />
result is the same -- we get support for all byte- and text-oriented<br />
I/O operations. The "bufferMemoryStream" operation can be applied to any<br />
memory-based stream to add buffering to it. The "bufferMemoryStreamUnchecked" <br />
operation (which implements the third buffering<br />
transformer) can be used instead, if you can guarantee that I/O<br />
operations can't overflow the used buffer.<br />
<br />
=== Encoding ===<br />
<br />
The Char encoding transformer allows you to encode each Char written to the<br />
stream as a sequence of bytes, implementing UTF and other encodings.<br />
This transformer can be applied to any stream implementing<br />
vGetByte/vPutByte operations and in return it implements<br />
vGetChar/vPutChar and all other text-oriented operations. This<br />
transformer can be applied to a stream with the "withEncoding encoding"<br />
operation, where `encoding` may be `latin1`, `utf8` or any other<br />
encoding that you (or a 3rd-party lib) implement. Look at the<br />
"Data.CharEncoding" module to see how to implement new encodings.<br />
Encoding of streams created with the "withEncoding" operation can be<br />
changed at any moment with "vSetEncoding" and queried with<br />
"vGetEncoding". See examples of their usage in the file<br />
"Examples/CharEncoding.hs"<br />
<br />
=== Locking ===<br />
<br />
The locking transformer ensures that the stream is properly shared by <br />
several threads. You already know enough about its basic usage --<br />
"withLocking" applies this transformer to the stream and all the<br />
required locking is performed automagically. You can also use "lock"<br />
operations to acquire the lock explicitly during multiple operations:<br />
<br />
<haskell><br />
lock h $ \h -> do<br />
savedpos <- vTell h<br />
vSeek h AbsoluteSeek 100<br />
vPutStr h ":-)"<br />
vSeek h AbsoluteSeek savedpos<br />
</haskell><br />
<br />
See the file "Examples/Locking.hs" for examples of using locking transformer.<br />
<br />
=== Attaching user data ===<br />
<br />
This transformer allows you to attach arbitrary data to any Stream. It does <br />
nothing extraordinary except that the stream with attached data is the proper <br />
Stream, again. See example of its usage in the file "Examples/UserData.hs" <br />
<br />
== Overview of Stream Types ==<br />
<br />
=== Handle (legacy way to access files/sockets) ===<br />
<br />
"Handle" is an instance of the Stream class, with a straightforward implementation. <br />
You can use the<br />
Char encoding transformer with Handles. Although Handles implement<br />
buffering and locking by themselves, you may also be interested in<br />
applying these transformers to the Handle type. This has<br />
benefits -- "bufferBlockStream" works faster than internal Handle<br />
buffering, and the locking transformer enables the use of a "lock" operation to<br />
create a lock around a sequence of operations. Moreover, the locking<br />
transformer should be used to ensure proper multi-threading operation<br />
of Handle with added encoding or buffering facilities.<br />
<br />
=== FD (new way to access files) ===<br />
<br />
The new method of using files, independent of the existing I/O<br />
library, is implemented with the FD type. FD is just an Int representing a<br />
POSIX file descriptor and the FD type implements only basic Stream I/O<br />
operations - vGetBuf and vPutBuf. So, to create a full-featured FD-based<br />
stream, you need to apply buffering transformers. Therefore, the library<br />
defines two ways to open files with FD - openRawFD/openRawBinaryFD<br />
just creates FD, while openFD/openBinaryFD creates FD and immediatelly<br />
apply buffering transformer (bufferBlockStream) to it. In most cases<br />
you will use the latter operations. Both pairs mimic the arguments and<br />
behaviour of well-known Handle operations openFile/openBinaryFile, so<br />
you already know how to use them. Other transformers may be used then<br />
as you need. So, abovementioned example can be abbreviated to:<br />
<br />
<haskell><br />
h <- openFD "test" WriteMode<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
</haskell><br />
<br />
Thus, to switch from the existing I/O library to using Streams, you<br />
need only to replace "h" with "v" in the names of Handle operations, and<br />
replace openFile/openBinaryFile calls with openFD/openBinaryFD while<br />
adding the "withLocking" transformer to files used in multiple threads.<br />
That's all! <br />
<br />
<br />
For example, the following code:<br />
<br />
<haskell><br />
h <- openFile "test" ReadMode<br />
text <- hGetContents h<br />
hClose h<br />
</haskell><br />
<br />
should be translated to:<br />
<br />
<haskell><br />
h <- openFD "test" ReadMode<br />
-- >>= withLocking -- needed only for multi-threaded usage<br />
text <- vGetContents h<br />
vClose h<br />
</haskell><br />
<br />
<br />
File "Examples/FD.hs" will show you the FD usage.<br />
<br />
<br />
=== MemBuf (memory-resident stream) ===<br />
<br />
MemBuf is a stream type, that keeps its contents in memory buffer.<br />
There are two types of MemBufs you can create - you can either open<br />
existing memory buffer with "openMemBuf ptr size" or create new one<br />
with "createMemBuf initsize". MemBuf opened by "openMemBuf" will be<br />
never resized or moved in memory, and will not be freed by "vClose".<br />
MemBuf created by "createMemBuf" will grow as needed, can be manually<br />
resized by "vSetFileSize" operation, and is automatically freed by<br />
"vClose".<br />
<br />
Actually, raw MemBufs created by the "createRawMemBuf" and "openRawMemBuf"<br />
operations, while createMemBuf/openMemBuf incorporate an additional<br />
"bufferMemoryStream" call (as you should remember, buffering adds vGetChar,<br />
vPutStr and other text- and byte-I/O operations on top of vReceiveBuf<br />
and vSendBuf). You can also apply Char encoding and locking<br />
transformers to these streams. The "saveToFile" and "readFromFile" operations <br />
provide an easy way to save/restore buffer contents in a file. <br />
<br />
File "Examples/MemBuf.hs" demonstrates the usage of MemBuf.<br />
<br />
=== FunctionsMemoryStream ===<br />
<br />
This Stream type allows implementation of arbitrary streams, just by<br />
providing three functions that implement vReceiveBuf, vSendBuf and cleanup<br />
operations. It seems that this Stream type is of interest only for my<br />
own program and can be scrutinized only as example of creating 3rd-party<br />
Stream types. It is named "FunctionsMemoryStream", see the sources if you<br />
are interested.<br />
<br />
=== StringReader & StringBuffer (String-based streams) ===<br />
<br />
Four remaining Stream types were part of the HVIO module and I copied their<br />
description from there:<br />
<br />
In addition to Handle, there are several pre-defined stream types for<br />
your use. 'StringReader' is a particularly interesting one. At<br />
creation time, you pass it a String. Its contents are read lazily<br />
whenever a read call is made. It can be used, therefore, to implement<br />
filters (simply initialize it with the result from, say, a map over<br />
hGetContents from another Stream object), codecs, and simple I/O<br />
testing. Because it is lazy, it needs not hold the entire string in<br />
memory. You can create a 'StringReader' with a call to<br />
'newStringReader'.<br />
<br />
'StringBuffer' is a similar type, but with a different purpose. It<br />
provides a full interface like Handle (it supports read, write and<br />
seek operations). However, it maintains an in-memory buffer with the<br />
contents of the file, rather than an actual on-disk file. You can<br />
access the entire contents of this buffer at any time. This can be<br />
quite useful for testing I/O code, or for cases where existing APIs<br />
use I/O, but you prefer a String representation. Note however that<br />
this stream type is very inefficient. You can create a 'StringBuffer'<br />
with a call to 'newStringBuffer'.<br />
<br />
One significant improvement over the original HVIO library is that<br />
'StringReader' and 'StringBuffer' can work not only in IO, but also in<br />
ST monad.<br />
<br />
=== Pipes (passing data between Haskell threads) ===<br />
<br />
Finally, there are pipes. These pipes are analogous to the Unix pipes<br />
that are available from System.Posix, but don't require Unix and work<br />
only in Haskell. When you create a pipe, you actually get two Stream<br />
objects: a 'PipeReader' and a 'PipeWriter'. You must use the<br />
'PipeWriter' in one thread and the 'PipeReader' in another thread.<br />
Data that's written to the 'PipeWriter' will then be available for<br />
reading with the 'PipeReader'. The pipes are implemented completely<br />
with existing Haskell threading primitives, and require no special<br />
operating system support. Unlike Unix pipes, these pipes cannot be<br />
used across a fork(). Also unlike Unix pipes, these pipes are<br />
portable and interact well with Haskell threads. A new pipe can be<br />
created with a call to 'newHVIOPipe'.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/Streams&diff=4148Library/Streams2006-05-21T14:41:05Z<p>HenkJanVanTuyl: </p>
<hr />
<div>== Introduction ==<br />
<br />
=== Streams: the extensible I/O library ===<br />
<br />
I have developed a new I/O library that IMHO is so sharp that it can<br />
eventually replace the current I/O facilities based on using Handles.<br />
The main advantage of the new library is its strong modular design using<br />
typeclasses. The library consists of small independent modules, each<br />
implementing one type of stream (file, memory buffer, pipe) or one<br />
part of common stream functionality (buffering, Char encoding,<br />
locking). 3rd-party libs can easily add new stream types and new common<br />
functionality. Other benefits of the new library include support for<br />
streams functioning in any monad, Hugs and GHC compatibility, high<br />
speed and an easy migration path from the existing I/O library.<br />
<br />
The Streams library is heavily based on the HVIO module written by John<br />
Goerzen. I especially want to thank John for his clever design and<br />
implementation. Really, I just renamed HVIO to Stream and presented<br />
this as my own work. :) Further development direction was inspired<br />
by the "New I/O library" written by Simon Marlow.<br />
<br />
=== Simple Streams ===<br />
<br />
The key concept of the lib is the Stream class, whose interface mimics<br />
familiar interface for Handles, just with "h" replaced with "v" in<br />
function names:<br />
<br />
<haskell><br />
class (Monad m) => Stream m h where<br />
vPutStrLn :: h -> String -> m ()<br />
vGetContents :: h -> m String<br />
vIsEOF :: h -> m Bool<br />
vClose :: h -> m ()<br />
....................<br />
</haskell><br />
<br />
This means that you already know how to use any stream! The Stream interface<br />
currently has 8 implementations: a Handle itself, raw files, pipes,<br />
memory buffers and string buffers. Future plans include support for<br />
memory-mapped files, sockets, circular memory buffers for interprocess<br />
communication and UArray-based streams.<br />
<br />
By themselves, these Stream implementations are rather simple. Basically,<br />
to implement new Stream type, it's enough to provide vPutBuf/vGetBuf<br />
operations, or even vGetChar/vPutChar. The latter way, although <br />
inefficient, allows us to implement streams that can work in any monad.<br />
StringReader and StringBuffer streams use this to provide string-based<br />
Stream class implementations both for IO and ST monads. Yes, you can<br />
use the full power of Stream operations inside the ST monad!<br />
<br />
=== Layers of Functionality ===<br />
<br />
All additional functionality is implemented via Stream Transformers,<br />
which are just parameterized Streams, whose parameters also<br />
implement the Stream interface. This allows you to apply any number of stream<br />
transformers to the raw stream and then use the result as an ordinary<br />
Stream. For example:<br />
<br />
<haskell><br />
h <- openRawFD "test" WriteMode<br />
>>= bufferBlockStream<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
</haskell><br />
<br />
This code creates a new FD, which represents a raw file, and then adds<br />
to this Stream buffering, Char encoding and locking functionality. The<br />
result type of "h" is something like this:<br />
<br />
<haskell><br />
WithLocking (WithEncoding (BufferedBlockStream FD))<br />
</haskell><br />
<br />
The complete type, as well as all the intermediate types, implements the Stream<br />
interface. Each transformer intercepts operations corresponding to its<br />
nature, and passes the rest through. For example, the encoding transformer<br />
intercepts only vGetChar/vPutChar operations and translates them to<br />
the sequences of vGetByte/vPutByte calls of the lower-level stream.<br />
The locking transformer just wraps any operation in the locking wrapper.<br />
<br />
We can trace, for example, the execution of a "vPutBuf" operation on the<br />
above-constructed Stream. First, the locking transformer acquires a lock<br />
and then passes this call to the next level. Then the encoding transformer does<br />
nothing and passes this call to the next level. The buffering<br />
transformer flushes the current buffer and passes the call further.<br />
Finally, FD itself performs the operation after all these<br />
preparations and on the returning path, the locking transformer release<br />
its lock.<br />
<br />
As another example, the "vPutChar" call on this Stream is<br />
transformed (after locking) into several "vPutByte" calls by the<br />
encoding transformer, and these bytes go to the buffer in the<br />
buffering transformer, with or without a subsequent call to the FD's<br />
"vPutBuf".<br />
<br />
=== Modularity ===<br />
<br />
As you can see, stream transformers really are independent of each<br />
other. This allows you to use them on any stream and in any combination<br />
(but you should apply them in proper order - buffering, then Char<br />
encoding, then locking). As a result, you can apply to the stream<br />
only the transformers that you really need. If you don't use the<br />
stream in multiple threads, you don't need to apply the locking<br />
transformer. If you don't use any encodings other than Latin-1 -- or<br />
don't use text I/O at all -- you don't need an encoding transformer.<br />
Moreover, you may not even need to know anything about the UserData transformer<br />
until you actually need to use it :)<br />
<br />
Both streams and stream transformers can be implemented by 3rd-party<br />
libraries. Streams and transformers from arbitrary libraries will<br />
seamlessly work together as long as they properly implement the Stream<br />
interface. My future plans include implementation of an on-the-fly<br />
(de)compression transformer and I will be happy to see 3rd-party<br />
transformers that intercept vGetBuf/vPutBuf calls and use select(),<br />
kqueue() and other methods to overlap I/O operations.<br />
<br />
=== Speed ===<br />
<br />
A quick comment about speed: it's fast enough -- 10-50 MB/s (depending<br />
on the type of operation) on a 1GHz cpu. The Handle operations, for comparison, <br />
show speed of 1-10 mb/s on the same computer. But that don't means that each <br />
and any operation in new library is 10 times faster. Strict I/O (including <br />
vGetChar/vPutChar) is a LOT faster. I included a demonstration of this <br />
fascinating speed as "Examples/wc.hs". If you need a really high speed, <br />
don't forget to increase buffer size with "vSetBuffering".<br />
<br />
On the other side, lazy I/O (including any operations that receive or return <br />
strings) show only modest speedup. This is limited by Haskell/GHC itself and <br />
I can't do much to get around these limits. Instead, I plan to provide support <br />
for I/O using packed strings. This will allow to write I/O-intensive Haskell <br />
programs that are as fast as their C counterparts.<br />
<br />
Other sources of slowness includes using of locking transformer (if you need <br />
to do this, try use "lock" around speed-critical algorithms) and complex class <br />
structure, what may be avoided by using "forall" types (I'm not sure, Simon <br />
Marlow can enlighten this topic).<br />
<br />
The library includes benchmarking code in the file "Examples/StreamsBenchmark.hs"<br />
<br />
=== Support for GHC, Hugs and other compilers ===<br />
<br />
The library is compatible with GHC 6.4<br />
<br />
<br />
The library fully supports Hugs 2003-2006, but<br />
<br />
1) support for FD and MMFile is temporarily disabled because I don't know how <br />
to build DLLs<br />
<br />
2) Hugs 2003 doesn't include support for "instance Bits Word" and vGetBuf/vPutBuf, <br />
so you need to add these implementations manually or delete the lines that use it <br />
(look for "2003" in the sources)<br />
<br />
3) WinHugs doesn't support preprocessing, so I included the MakeHugs.cmd script <br />
to preprocess source files using cpphs<br />
<br />
<br />
Main disadvantage of the library is that it supports only Hugs and GHC<br />
because of using extensions in type classe system (namely, MPTC+FD). I think that it<br />
can be made H98-compatible at the cost of excluding support for non-IO<br />
monads. I will try to make such a stripped version for other compilers<br />
if people are interested.<br />
<br />
=== Downloading and installation ===<br />
<br />
You can download last version of library as http://freearc.narod.ru/Streams.tar.gz<br />
<br />
<br />
The library currently doesn't support Cabal or any other automatic installation<br />
system. It's just a plain collection of modules, so to use it, you can either<br />
copy directories "Data" and "System" to the directory where your application<br />
lies or, preferably, add to your ghc's compilation command the "-i" switch<br />
with the path to directory where you unpacked library. For example:<br />
<br />
ghc --make -i/Haskell/StreamsLib YourProgram.hs<br />
<br />
In the latter case, GHC will look, for example, for module "Data.Ref" in file<br />
/Haskell/StreamsLib/Data/Ref.hs<br />
<br />
<br />
The same applies to Hugs, but in this case, the corresponding switch is named "-P":<br />
runhugs -P/Haskell/StreamsLib YourProgram.hs<br />
For WinHugs, you can setup this path through the "Options" dialog.<br />
<br />
<br />
Directory "Examples" contains examples of using library, you should copy<br />
files from this directory to the root library directory in order to compile them.<br />
<br />
=== Stage of Development ===<br />
<br />
The library is currently at the beta stage. It contains a number of<br />
known minor problems and an unknown number of yet-to-be-discovered bugs.<br />
It is not properly documented, doesn't include QuickCheck tests, is not<br />
cabalized, and not all "h*" operations have their "v*" equivalents yet.<br />
If anyone wants to join this effort in order to help fix these oddities<br />
and prepare the lib for inclusion in the standard libraries suite, I would<br />
be really happy. :) I will also be happy (although much less ;) to see<br />
bug reports and suggestions about its interface and internal<br />
organization. It's just a first public version, so we still can change<br />
everything here!<br />
<br />
In particular, this wiki page is an official library documentation. <br />
Please continue to improve it and add more information about using the library.<br />
Feel free to ask me about library usage via email: [mailto:Bulat.Ziganshin@gmail.com Bulat.Ziganshin@gmail.com]<br />
<br />
<br />
== Overview of Stream Transformers ==<br />
<br />
=== Buffering ===<br />
<br />
There are three buffering transformers. Each buffering transformer<br />
implements support for vGetByte, vPutChar, vGetContents and other<br />
byte- and text-oriented operations for the streams, which by themselves<br />
support only vGetBuf/vPutBuf (or vReceiveBuf/vSendBuf) operations.<br />
<br />
The first transformer can be applied to any stream supporting<br />
vGetBuf/vPutBuf. This is applied by the operation "bufferBlockStream". The<br />
well-known vSetBuffering/vGetBuffering operations are intercepted by<br />
this transformer and used to control buffer size. At this moment, only<br />
BlockBuffering is implemented, while LineBuffering and NoBuffering are<br />
only in the planning stages.<br />
<br />
Two other transformers can be applied to streams that implement<br />
vReceiveBuf/vSendBuf operations -- that is, streams whose data<br />
reside in memory, including in-memory streams and memory-mapped<br />
files. In these cases, the buffering transformer doesn't need to allocate<br />
a buffer itself, it just requests from the underlying stream the address and<br />
size of the next available portion of data. Nevertheless, the final<br />
result is the same -- we get support for all byte- and text-oriented<br />
I/O operations. The "bufferMemoryStream" operation can be applied to any<br />
memory-based stream to add buffering to it. The "bufferMemoryStreamUnchecked" <br />
operation (which implements the third buffering<br />
transformer) can be used instead, if you can guarantee that I/O<br />
operations can't overflow the used buffer.<br />
<br />
=== Encoding ===<br />
<br />
The Char encoding transformer allows you to encode each Char written to the<br />
stream as a sequence of bytes, implementing UTF and other encodings.<br />
This transformer can be applied to any stream implementing<br />
vGetByte/vPutByte operations and in return it implements<br />
vGetChar/vPutChar and all other text-oriented operations. This<br />
transformer can be applied to a stream with the "withEncoding encoding"<br />
operation, where `encoding` may be `latin1`, `utf8` or any other<br />
encoding that you (or a 3rd-party lib) implement. Look at the<br />
"Data.CharEncoding" module to see how to implement new encodings.<br />
Encoding of streams created with the "withEncoding" operation can be<br />
changed at any moment with "vSetEncoding" and queried with<br />
"vGetEncoding". See examples of their usage in the file<br />
"Examples/CharEncoding.hs"<br />
<br />
=== Locking ===<br />
<br />
The locking transformer ensures that the stream is properly shared by <br />
several threads. You already know enough about its basic usage --<br />
"withLocking" applies this transformer to the stream and all the<br />
required locking is performed automagically. You can also use "lock"<br />
operations to acquire the lock explicitly during multiple operations:<br />
<br />
<haskell><br />
lock h $ \h -> do<br />
savedpos <- vTell h<br />
vSeek h AbsoluteSeek 100<br />
vPutStr h ":-)"<br />
vSeek h AbsoluteSeek savedpos<br />
</haskell><br />
<br />
See the file "Examples/Locking.hs" for examples of using locking transformer.<br />
<br />
=== Attaching user data ===<br />
<br />
This transformer allows you to attach arbitrary data to any Stream. It does <br />
nothing extraordinary except that the stream with attached data is the proper <br />
Stream, again. See example of its usage in the file "Examples/UserData.hs" <br />
<br />
== Overview of Stream Types ==<br />
<br />
=== Handle (legacy way to access files/sockets) ===<br />
<br />
"Handle" is an instance of the Stream class, with a straightforward implementation. <br />
You can use the<br />
Char encoding transformer with Handles. Although Handles implement<br />
buffering and locking by themselves, you may also be interested in<br />
applying these transformers to the Handle type. This has<br />
benefits -- "bufferBlockStream" works faster than internal Handle<br />
buffering, and the locking transformer enables the use of a "lock" operation to<br />
create a lock around a sequence of operations. Moreover, the locking<br />
transformer should be used to ensure proper multi-threading operation<br />
of Handle with added encoding or buffering facilities.<br />
<br />
=== FD (new way to access files) ===<br />
<br />
The new method of using files, independent of the existing I/O<br />
library, is implemented with the FD type. FD is just an Int representing a<br />
POSIX file descriptor and the FD type implements only basic Stream I/O<br />
operations - vGetBuf and vPutBuf. So, to create a full-featured FD-based<br />
stream, you need to apply buffering transformers. Therefore, the library<br />
defines two ways to open files with FD - openRawFD/openRawBinaryFD<br />
just creates FD, while openFD/openBinaryFD creates FD and immediatelly<br />
apply buffering transformer (bufferBlockStream) to it. In most cases<br />
you will use the latter operations. Both pairs mimic the arguments and<br />
behaviour of well-known Handle operations openFile/openBinaryFile, so<br />
you already know how to use them. Other transformers may be used then<br />
as you need. So, abovementioned example can be abbreviated to:<br />
<br />
<haskell><br />
h <- openFD "test" WriteMode<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
</haskell><br />
<br />
Thus, to switch from the existing I/O library to using Streams, you<br />
need only to replace "h" with "v" in the names of Handle operations, and<br />
replace openFile/openBinaryFile calls with openFD/openBinaryFD while<br />
adding the "withLocking" transformer to files used in multiple threads.<br />
That's all! <br />
<br />
<br />
For example, the following code:<br />
<br />
<haskell><br />
h <- openFile "test" ReadMode<br />
text <- hGetContents h<br />
hClose h<br />
</haskell><br />
<br />
should be translated to:<br />
<br />
<haskell><br />
h <- openFD "test" ReadMode<br />
-- >>= withLocking -- needed only for multi-threaded usage<br />
text <- vGetContents h<br />
vClose h<br />
</haskell><br />
<br />
<br />
File "Examples/FD.hs" will show you the FD usage.<br />
<br />
<br />
=== MemBuf (memory-resident stream) ===<br />
<br />
MemBuf is a stream type, that keeps its contents in memory buffer.<br />
There are two types of MemBufs you can create - you can either open<br />
existing memory buffer with "openMemBuf ptr size" or create new one<br />
with "createMemBuf initsize". MemBuf opened by "openMemBuf" will be<br />
never resized or moved in memory, and will not be freed by "vClose".<br />
MemBuf created by "createMemBuf" will grow as needed, can be manually<br />
resized by "vSetFileSize" operation, and is automatically freed by<br />
"vClose".<br />
<br />
Actually, raw MemBufs created by the "createRawMemBuf" and "openRawMemBuf"<br />
operations, while createMemBuf/openMemBuf incorporate an additional<br />
"bufferMemoryStream" call (as you should remember, buffering adds vGetChar,<br />
vPutStr and other text- and byte-I/O operations on top of vReceiveBuf<br />
and vSendBuf). You can also apply Char encoding and locking<br />
transformers to these streams. The "saveToFile" and "readFromFile" operations <br />
provide an easy way to save/restore buffer contents in a file. <br />
<br />
File "Examples/MemBuf.hs" demonstrates the usage of MemBuf.<br />
<br />
=== FunctionsMemoryStream ===<br />
<br />
This Stream type allows implementation of arbitrary streams, just by<br />
providing three functions that implement vReceiveBuf, vSendBuf and cleanup<br />
operations. It seems that this Stream type is of interest only for my<br />
own program and can be scrutinized only as example of creating 3rd-party<br />
Stream types. It is named "FunctionsMemoryStream", see the sources if you<br />
are interested.<br />
<br />
=== StringReader & StringBuffer (String-based streams) ===<br />
<br />
Four remaining Stream types were part of the HVIO module and I copied their<br />
description from there:<br />
<br />
In addition to Handle, there are several pre-defined stream types for<br />
your use. 'StringReader' is a particularly interesting one. At<br />
creation time, you pass it a String. Its contents are read lazily<br />
whenever a read call is made. It can be used, therefore, to implement<br />
filters (simply initialize it with the result from, say, a map over<br />
hGetContents from another Stream object), codecs, and simple I/O<br />
testing. Because it is lazy, it needs not hold the entire string in<br />
memory. You can create a 'StringReader' with a call to<br />
'newStringReader'.<br />
<br />
'StringBuffer' is a similar type, but with a different purpose. It<br />
provides a full interface like Handle (it supports read, write and<br />
seek operations). However, it maintains an in-memory buffer with the<br />
contents of the file, rather than an actual on-disk file. You can<br />
access the entire contents of this buffer at any time. This can be<br />
quite useful for testing I/O code, or for cases where existing APIs<br />
use I/O, but you prefer a String representation. Note however that<br />
this stream type is very inefficient. You can create a 'StringBuffer'<br />
with a call to 'newStringBuffer'.<br />
<br />
One significant improvement over the original HVIO library is that<br />
'StringReader' and 'StringBuffer' can work not only in IO, but also in<br />
ST monad.<br />
<br />
=== Pipes (passing data between Haskell threads) ===<br />
<br />
Finally, there are pipes. These pipes are analogous to the Unix pipes<br />
that are available from System.Posix, but don't require Unix and work<br />
only in Haskell. When you create a pipe, you actually get two Stream<br />
objects: a 'PipeReader' and a 'PipeWriter'. You must use the<br />
'PipeWriter' in one thread and the 'PipeReader' in another thread.<br />
Data that's written to the 'PipeWriter' will then be available for<br />
reading with the 'PipeReader'. The pipes are implemented completely<br />
with existing Haskell threading primitives, and require no special<br />
operating system support. Unlike Unix pipes, these pipes cannot be<br />
used across a fork(). Also unlike Unix pipes, these pipes are<br />
portable and interact well with Haskell threads. A new pipe can be<br />
created with a call to 'newHVIOPipe'.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Learning_Haskell&diff=3908Learning Haskell2006-04-30T15:32:47Z<p>HenkJanVanTuyl: Added a link to "A tour of the Haskell Monad functions"</p>
<hr />
<div>__NOTOC__<br />
<br />
=Learning Haskell=<br />
<br />
==Introduction==<br />
<br />
Haskell is a general purpose, purely functional programming language. This portal points to places where you can go if you want to learn Haskell. <br />
<br />
The [[Introduction]] on the [[Haskell]] homepage tells you that Haskell gives you: substantially increased programmer productivity; shorter, clearer, and more maintainable code; fewer errors; higher reliability; a smaller "semantic gap" between the programmer and the language; shorter lead times.<br />
<br />
There is an old -- but still relevant -- paper about [http://www.md.chalmers.se/~rjmh/Papers/whyfp.html Why Functional Programming Matters] by John Hughes. More recently Sebastian Sylvan wrote an article about [[Why Haskell Matters]]. And there is a [http://www.haskell.org/haskellwiki/Comparison table comparing Haskell to other functional languages]. Many questions about functional programming are answered by the [http://www.cs.nott.ac.uk/~gmh//faq.html comp.lang.functional FAQ].<br />
<br />
==Implementations==<br />
<br />
{| border=1 cellspacing=0 cellpadding=5 bgcolor=#FFFFFF width=100%|<br />
|-<br />
| <br />
! Messages<br />
! Size<br />
! Tools<br />
! Remarks <br />
|-<br />
| [http://www.haskell.org/hugs/ Hugs]<br />
| +/-<br />
| ++<br />
| -<br />
| Fast compilation; used a lot for learning Haskell<br />
|-<br />
| [http://www.haskell.org/ghc/ GHC]<br />
| +<br />
| -<br />
| ++<br />
| Many language extensions; generated code is very fast<br />
|-<br />
| [http://www.cs.york.ac.uk/fp/nhc98/ NHC]<br />
| ?<br />
| +<br />
| ++<br />
| Profiling, debugging, tracing<br />
|-<br />
| [http://www.cs.uu.nl/helium/ Helium]<br />
| ++<br />
| ++<br />
| -<br />
| No type classes (yet!) and thus incompatible with most material on this site. Made for teaching/learning.<br />
|}<br />
<br />
==Books and tutorials==<br />
<br />
{| border=1 cellspacing=0 cellpadding=5 width=100% valign=top style="text-align:left" |<br />
! Textbooks<br />
! Tutorials<br />
<br />
|- style="vertical-align:top"<br />
<br />
|<br />
<br />
* [http://www.haskell.org/soe The Haskell School of Expression]<br />
* [http://www.cs.ukc.ac.uk/people/staff/sjt/craft2e/ Haskell: the Craft of Functional Programming]<br />
* [http://www.prenhall.com/allbooks/ptr_0134843460.html Introduction to Functional Programming using Haskell]<br />
* [http://books.cambridge.org/0521277248.htm An Introduction to Functional Programming Systems Using Haskell]<br />
* [http://www.iro.umontreal.ca/~lapalme/Algorithms-functional.html Algorithms: A functional programming approach]<br />
* [http://homepages.cwi.nl/~jve/HR/ The Haskell Road to Logic, Maths, and Programming]<br />
<br />
|<br />
<br />
* [http://www.isi.edu/~hdaume/htut Yet Another Haskell Tutorial]<br />
* [ftp://ftp.geoinfo.tuwien.ac.at/navratil/HaskellTutorial.pdf Haskell-Tutorial]<br />
* [http://www.informatik.uni-bonn.de/~ralf/teaching/Hskurs_toc.html Online Haskell Course] (German)<br />
* [http://www.haskell.org/tutorial/ A Gentle Introduction to Haskell]<br />
* [http://www.cs.ou.edu/~rlpage/fpclassCurrent/textbook/haskell.shtml Two dozen short lessons]<br />
* [http://halogen.note.amherst.edu/%7Ejdtang/scheme_in_48/tutorial/overview.html Tackling the Awkward Squad] (on monads and IO, concurrency and exceptions)<br />
* [http://halogen.note.amherst.edu/%7Ejdtang/scheme_in_48/tutorial/overview.html Write Yourself a Scheme in 48 Hours]<br />
* [http://www.haskell.org/haskellwiki/Hitchhikers_Guide_to_the_Haskell Hitchhikers Guide to Haskell]<br />
<br />
|-<br />
<br />
! Reference<br />
! Course Material<br />
<br />
|- style="vertical-align:top"<br />
<br />
|<br />
<br />
* [http://www.haskell.org/hawiki/HaskellNewbie Haskell Newbie]<br />
* [http://www.cs.uu.nl/~afie/haskell/tourofsyntax.html Tour of the Haskell Syntax]<br />
* [http://zvon.org/other/haskell/Outputglobal/index.html Haskell Reference]<br />
* [http://www.haskell.org/haskellwiki/Reference_card Haskell Reference Card]<br />
* [http://www.cs.uu.nl/~afie/haskell/tourofprelude.html Tour of the Haskell Prelude]<br />
* [http://members.chello.nl/hjgtuyl/tourdemonad.html A tour of the Haskell Monad functions]<br />
* [http://www.cs.uu.nl/helium/docs/TourOfPrelude.html Tour of the Helium Prelude]<br />
* [http://www.cs.ukc.ac.uk/people/staff/sjt/craft2e/errors/allErrors.html Some common Hugs error messages]<br />
* [http://www.haskell.org/haskellwiki/Category:Idioms Some Haskell Idioms]<br />
* [http://www.haskell.org/hawiki Questions and Answers (old Haskell wiki)]<br />
<br />
|<br />
<br />
* [http://www.cs.chalmers.se/Cs/Grundutb/Kurser/d1pt/d1pta/external.html Programming in Haskell, Chalmers]<br />
* [http://www.cs.caltech.edu/courses/cs11/material/haskell/index.html CS 11 Caltech]<br />
* [http://www.cs.uu.nl/docs/vakken/lfp/ Functional programming]: course notes ([http://www.cs.uu.nl/~jeroen/courses/fp-eng.pdf English], [http://www.cs.uu.nl/~jeroen/courses/fp-nl.pdf Dutch], [http://www.cs.uu.nl/~jeroen/courses/fp-sp.pdf Spanish]), slides in Dutch<br />
* [http://www.cse.unsw.edu.au/~cs1011/ CS1011]: Tutorials, lab exercises and solutions<br />
<br />
|}<br />
<br />
<br />
Check [http://www.haskell.org/haskellwiki/Books_and_tutorials Books and tutorials] for a more comprehensive list.<br />
<br />
(perhaps these pages can be merged somehow, or the more introductory material can go on this page, and the advanced books and papers can go on a different page?)</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Books&diff=3907Books2006-04-30T15:04:27Z<p>HenkJanVanTuyl: Added a link to "A tour of the Haskell Monad functions"</p>
<hr />
<div>Books and tutorials covering many aspects of Haskell.<br />
<br />
=Language Definition=<br />
<br />
<DL><br />
<DT>Simon Peyton Jones: [http://titles.cambridge.org/catalogue.asp?isbn=0521826144 <EM>Haskell 98 Language and Libraries</EM>], Cambridge University Press, 2003, Hardback, 272 pages, ISBN: 0521826144, £35.00<br />
<BR><br />
<BLOCKQUOTE><br />
<B>Book Description</B><BR> <br />
Haskell is the world's leading lazy functional programming language,<br />
widely used for teaching, research, and applications. The language<br />
continues to develop rapidly, but in 1998 the community decided to<br />
capture a stable snapshot of the language: Haskell 98. All Haskell<br />
compilers support Haskell 98, so practitioners and educators alike<br />
have a stable base for their work. This book constitutes the agreed<br />
definition of the Haskell 98, both the language itself and its<br />
supporting libraries. It has been considerably revised and refined<br />
since the original definition, and appears in print for the first<br />
time. It should be a standard reference work for anyone involved in<br />
research, teaching, or application of Haskell.<br />
</BLOCKQUOTE> <br />
The entire language definition is also available online:<br />
[[Language_and_library_specification|Language and library specification]]<br />
</DT><br />
</DL><br />
<br />
= Textbooks=<br />
<br />
<DL><br />
<DT>Paul Hudak: [http://www.haskell.org/soe <EM>The Haskell School of Expression: Learning Functional Programming through Multimedia</EM>], Cambridge University Press, New York, 2000, 416<br />
pp, 15 line diagrams, 75 exercises, Paperback $29.95, ISBN:<br />
0521644089, Hardback $74.95, ISBN: 0521643384<br />
<blockquote><br />
<B>Book Description</B><BR> <br />
This book teaches functional programming as a way of thinking and<br />
problem solving, using Haskell, the most popular purely functional<br />
language. Rather than using the conventional mathematical examples<br />
commonly found in other programming language textbooks, the author<br />
draws examples from multimedia applications, including graphics,<br />
animation, and computer music, thus rewarding the reader with working<br />
programs for inherently more interesting applications. Aimed at both<br />
beginning and advanced programmers, this tutorial begins with a gentle<br />
introduction to functional programming and moves rapidly on to more<br />
advanced topics. An underlying theme is the design and implementation<br />
of domain specific languages, using three examples: FAL (a Functional<br />
Animation Language), IRL (an Imperative Robot Language), and MDL (a<br />
Music Description Language). Details about programming in Haskell<br />
are presented in boxes throughout the text so they can be easily<br />
referred to and found quickly.<br />
<br />
The book's Web Site contains source files for all programs in the<br />
text, as well as the graphics libraries to run them under Windows and<br />
Linux platforms. It also contains PowerPoint slides useful for<br />
teaching a course using the textbook.<br />
</blockquote><br />
<DT>Simon Thompson: [http://www.cs.ukc.ac.uk/people/staff/sjt/craft2e/ <EM>Haskell: The Craft of Functional Programming</EM>], Second Edition,<br />
Addison-Wesley, 507&nbsp;pages, paperback, 1999. ISBN<br />
0-201-34275-8.<br />
<blockquote><br />
<B>Book Description</B><BR> <br />
The second edition of Haskell: The Craft of Functional Programming is essential reading for beginners to functional programming and newcomers to the Haskell programming language. The emphasis is on the process of crafting programs and the text contains many examples and running case studies, as well as advice an program design, testing, problem solving and how to avoid common pitfalls. <br />
<br />
Building on the strengths of the first edition, the book includes many new and improved features: <br />
*Complete coverage of Haskell 98, the standard version of Haskell which will be stable and supported by implementations for years to come. <br />
*An emphasis on software engineering principles, encouraging a disciplined approach to building reusable libraries of software components. <br />
*Detailed coverage of the Hugs interpreter with an appendix covering other implementations. <br />
*A running case study of pictures emphasizes the built-in functions which appear in the standard prelude and libraries. It is also used to give an early preview of some of the more complex language features, such as high-order functions. <br />
*List comprehensions and the standard functions over lists are covered before recursion. <br />
*Early coverage of polymorphism supporting the "toolkit" approach and encouraging the resuse of built-in functions and types. <br />
*Extensive reference material containing details of further reading in books, journals and on the World Wide Web. <br />
*Accompanying Web Site supporting the book, containing all the program code, further teaching materials and other useful resources. <br />
<B>Synopsis</B><BR> <br />
This books introduces Haskell at a level appropriate for those with little or no prior experience of functional programming. The emphasis is on the process of crafting programs, solving problems, and avoiding common errors.<br />
</blockquote><br />
<br />
<DT>Richard Bird: [http://www.prenhall.com/allbooks/ptr_0134843460.html <EM>Introduction to Functional Programming using Haskell</EM>], 2nd edition, Prentice Hall Press, 1998, 460 pp., ISBN: 0-13-484346-0.<br />
<blockquote><br />
From the cover:<br />
<br />
After the success of the first edition, Introduction to Functional Programming using Haskell has been thoroughly updated and revised to provide a complete grounding in the principles and techniques of programming with functions.<br />
<br />
The second edition uses the popular language Haskell to express functional programs. There are new chapters on program optimisation, abstract datatypes in a functional setting, and programming in a monadic style. There are completely new case studies, and many new exercises.<br />
<br />
As in the first edition, there is an emphasis on the fundamental techniques for reasoning about functional programs, and for deriving them systematically from their specifications.<br />
<br />
The book is self-contained, assuming no prior knowledge of programming, and is suitable as an introductory undergraduate text for first- or second-year students.<br />
</blockquote><br />
<br />
<DT>Antony Davie: [http://www.cup.org/Titles/25/0521258308.html <EM>An Introduction to Functional Programming Systems Using Haskell</EM>], Cambridge University Press, 1992. ISBN 0-521-25830-8 (hardback). ISBN 0-521-27724-8 (paperback).<br />
<blockquote><br />
Cover:<br />
<br />
Functional programming is a style of programming that has become increasingly popular during the past few years.<br />
Applicative programs have the advantage of being almost immediately expressible as functional descriptions; they can<br />
be proved correct and transformed through the referential transparency property.<br />
<br />
This book presents the basic concepts of functional programming, using the language Haskell for examples. The author<br />
incorporates a discussion of lambda calculus and its relationship with Haskell, exploring the implications for<br />
parallelism. Contents: SASL for Beginners / Examples of SASL Programming / More Advanced Applicative Programming<br />
Techniques / Lambda Calculus / The Relationship Between Lambda Calculus and SASL / Program Transformation and<br />
Efficiency / Correctness, Equivalence and Program Verification / Landin's SECD Machine and Related<br />
Implementations / Further Implementation Techniques / Special Purpose Hardware / The Applicative Style of<br />
Semantics / Other Applicative Languages / Implications for Parallelism / Functional Programming in Von Neumann<br />
Languages <br />
</blockquote><br />
<br />
<DT>Fethi Rabhi and Guy Lapalme: [http://www.iro.umontreal.ca/~lapalme/Algorithms-functional.html <EM> Algorithms: A functional programming approach</EM>], <br />
Addison-Wesley, 235&nbsp;pages, paperback, 1999. ISBN<br />
0-201-59604-0<BR><br />
<BLOCKQUOTE><br />
<B>Book Description</B><BR> <br />
The authors challenge more traditional methods of teaching algorithms<br />
by using a functional programming context, with Haskell as an<br />
implementation language. This leads to smaller, clearer and more<br />
elegant programs which enable the programmer to understand the<br />
algorithm more quickly and to use that understanding to explore<br />
alternative solutions. <br><br />
<b>Key features:</b><br />
*Most chapters are self-contained and can be taught independently from each other.<br />
*All programs are in Haskell'98 and provided on a WWW site.<br />
*End of chapter exercises throughout.<br />
*Comprehensive index and bibliographical notes.<br />
<B>Synopsis</B><BR> <br />
The book is organised as a classic algorithms book according to topics<br />
such as Abstract Data Types, sorting and searching. It uses a<br />
succession of practical programming examples to develop in the reader<br />
problem-solving skills which can be easily transferred to other<br />
language paradigms. It also introduces the idea of capturing<br />
algorithmic design strategies (e.g. Divide-and-Conquer, Dynamic<br />
Programming) through higher-order functions.<br><br />
<b>Target audience</b><br><br />
The book is intended for computer science students taking algorithms<br />
and/or (basic or advanced) functional programming courses.<br />
</BLOCKQUOTE><br />
<br />
<dt>Jeremy Gibbons and Oege de Moor (eds.): [http://www.palgrave.com/catalogue/catalogue.asp?Title_Id=0333992857 <em>The Fun of Programming</em>],Palgrave, 2002, 288 pages. ISBN 0333992857.<br />
<blockquote><br />
<b>Book description:</b><br><br />
In this textbook, leading researchers give tutorial expositions on the current state of the art of functional<br />
programming. The text is suitable for an undergraduate course immediately following an introduction to<br />
functional programming, and also for self-study. All new concepts are illustrated by plentiful examples,<br />
as well as exercises. A website gives access to accompanying software.<br />
</blockquote><br />
<br />
<dt> Cordelia Hall and John O'Donnell: [http://www.dcs.gla.ac.uk/~jtod/discrete-mathematics/ <em>Discrete Mathematics Using a Computer</em>],<br />
Springer, 2000, 360 pages. ISBN 1-85233-089-9.<br />
<blockquote><br />
<b>Book description:</b><br><br />
This book introduces the main topics of discrete mathematics with a strong emphasis on<br />
applications to computer science. It uses computer programs to implement and illustrate<br />
the mathematical ideas, helping the reader to gain a concrete understanding of the<br />
abstract mathematics. The programs are also useful for practical calculations, and they<br />
can serve as a foundation for larger software packages. <br />
<br />
Designed for first and second year undergraduate students, the book is also ideally suited<br />
to self-study. No prior knowledge of functional programming is required; the book and<br />
the online documentation provide everything you will need. <br />
</blockquote><br />
<br />
<dt>Kees Doets and Jan van Eijck: [http://www.cwi.nl/~jve/HR <em>The Haskell Road to Logic, Maths and Programming</em>]. King's College Publications, London, 2004. ISBN 0-9543006-9-6 (14.00 pounds, $25.00).<br />
<blockquote><br />
<b>Book description:</b><br><br />
The purpose of this book is to teach logic and mathematical reasoning<br />
in practice, and to connect logical reasoning with computer<br />
programming. Throughout the text, abstract concepts are linked to<br />
concrete representations in Haskell. Everything one has to know about<br />
programming in Haskell to understand the examples in the book is<br />
explained as we go along, but we do not cover every aspect of the<br />
language. Haskell is a marvelous demonstration tool for logic and<br />
maths because its functional character allows implementations to<br />
remain very close to the concepts that get implemented, while the<br />
laziness permits smooth handling of infinite data structures.<br />
<br />
We do not assume that our readers have previous experience with either<br />
programming or construction of formal proofs. We do assume previous<br />
acquaintance with mathematical notation, at the level of secondary<br />
school mathematics. Wherever necessary, we will recall relevant <br />
facts. Everything one needs to know about mathematical<br />
reasoning or programming is explained as we go along. We do assume<br />
that our readers are able to retrieve software from the Internet and<br />
install it, and that they know how to use an editor for constructing<br />
program texts.<br />
<br />
After having worked through the material in the book, i.e., after<br />
having digested the text and having carried out a substantial number<br />
of the exercises, the reader will be able to write interesting<br />
programs, reason about their correctness, and document them in a clear<br />
fashion. The reader will also have learned how to set up mathematical<br />
proofs in a structured way, and how to read and digest mathematical<br />
proofs written by others.<br />
<br />
The book can be used as a course textbook, but since it comes with<br />
solutions to all exercises (electronically available from the authors<br />
upon request) it is also well suited for private study. The source<br />
code of all programs discussed in the text, a list of errata, <br />
further relevant material and an email link to the authors<br />
can be found [http://www.cwi.nl/~jve/HR here].<br />
</blockquote><br />
<br />
<dt>Simon Peyton Jones: <em>Implementation of Functional Programming Language</em>,Prentice-Hall, 1987. ISBN 0134533259.<br />
<br />
<dt>Simon Peyton Jones, David Lester: <em>Implementing Functional Languages</em>, 1992.<br><br />
<blockquote><br />
The book is out of print. The full sources and a postscript version are <br />
[http://research.microsoft.com/Users/simonpj/Papers/papers.html available for free].<br />
</blockquote><br />
<br />
<dt>Simon Thompson: <em>Type Theory and Functional Programming</em>, Addison-Wesley, 1991. ISBN 0-201-41667-0.<br />
<blockquote><br />
Now out of print, the original version is available [http://www.cs.kent.ac.uk/people/staff/sjt/TTFP/ here].<br />
<br />
<em>Preface</em>:<br />
Constructive Type theory has been a topic of research interest to computer scientists,<br />
mathematicians, logicians and philosophers for a number of years. For computer scientists it provides<br />
a framework which brings together logic and programming languages in a most elegant and fertile way:<br />
program development and verification can proceed within a single system. Viewed in a different way,<br />
type theory is a functional programming language with some novel features, such as the totality of<br />
all its functions, its expressive type system allowing functions whose result type depends upon the<br />
value of its input, and sophisticated modules and abstract types whose interfaces can contain logical<br />
assertions as well as signature information. A third point of view emphasizes that programs (or<br />
functions) can be extracted from proofs in the logic.<br />
</blockquote><br />
<br />
</DL><br />
<br />
=Tutorials and books=<br />
<br />
==Introductions to Haskell==<br />
<br />
;[http://www.haskell.org/tutorial/ A Gentle Introduction to Haskell] <br />
:By Paul Hudak, John Peterson, and Joseph H. Fasel. The title is a bit misleading. Some knowledge of another functional programming language is expected. The emphasis is on the type system and those features which are really new in Haskell (compared to other functional programming languages). A classic.<br />
<br />
;[http://www.isi.edu/~hdaume/htut/ Yet Another Haskell Tutorial] <br />
:By Hal Daume III et al. A recommended tutorial for Haskell that is still under construction but covers already much ground. Also a classic text.<br />
<br />
;[http://www.haskell.org/~pairwise/intro/intro.html Haskell Tutorial for C Programmers]<br />
:By Eric Etheridge. From the intro: "This tutorial assumes that the reader is familiar with C/C++, Python, Java, or Pascal. I am writing for you because it seems that no other tutorial was written to help students overcome the difficulty of moving from C/C++, Java, and the like to Haskell."<br />
<br />
;[http://www-106.ibm.com/developerworks/edu/os-dw-linuxhask-i.html Beginning Haskell] <br />
:From IBM developerWorks. This tutorial targets programmers of imperative languages wanting to learn about functional programming in the language Haskell. If you have programmed in languages such as C, Pascal, Fortran, C++, Java, Cobol, Ada, Perl, TCL, REXX, JavaScript, Visual Basic, or many others, you have been using an imperative paradigm. This tutorial provides a gentle introduction to the paradigm of functional programming, with specific illustrations in the Haskell 98 language. (Free registration required.)<br />
<br />
;[http://www.informatik.uni-bonn.de/~ralf/teaching/Hskurs_toc.html Online Haskell Course] <br />
:By Ralf Hinze (in German).<br />
<br />
;[http://www.cs.uu.nl/people/jeroen/courses/fp-eng.pdf Functional Programming]<br />
:By Jeroen Fokker, 1995. (153 pages, 600 KB). Textbook for learning functional programming with Gofer (an older implementation of Haskell). Here without Chapters&nbsp;6 and&nbsp;7. <br />
<br />
;[http://www.cs.chalmers.se/~rjmh/tutorials.html Tutorial Papers in Functional Programming].<br />
:A collection of links to other Haskell tutorials, from John Hughes.<br />
<br />
;[http://www.cs.ou.edu/cs1323h/textbook/haskell.shtml Two Dozen Short Lessons in Haskell] <br />
:By Rex Page. A draft of a textbook on functional programming, available by ftp. It calls for active participation from readers by omitting material at certain points and asking the reader to attempt to fill in the missing information based on knowledge they have already acquired. The missing information is then supplied on the reverse side of the page. <br />
<br />
;[http://www.cs.chalmers.se/~augustss/AFP/manuals/haskeller.dvi.gz The Little Haskeller] <br />
:By Cordelia Hall and John Hughes. 9. November 1993, 26 pages. An introduction using the Chalmers Haskell B interpreter (hbi). Beware that it relies very much on the user interface of hbi which is quite different for other Haskell systems, and the tutorials cover Haskell 1.2 , not Haskell 98.<br />
<br />
;[http://pleac.sourceforge.net/pleac_haskell/t1.html PLEAC-Haskell]<br />
:Following the Perl Cookbook (by Tom Christiansen and Nathan Torkington, published by O'Reilly) spirit, the PLEAC Project aims to gather fans of programming, in order to implement the solutions in other programming languages.<br />
<br />
;[ftp://ftp.geoinfo.tuwien.ac.at/navratil/HaskellTutorial.pdf Haskell-Tutorial] <br />
:By Damir Medak and Gerhard Navratil. The fundamentals of functional languages for beginners. <br />
<br />
;[http://www.reid-consulting-uk.ltd.uk/docs/ffi.html A Guide to Haskell's Foreign Function Interface]<br />
:A guide to using the foreign function interface extension, using the rich set of functions in the Foreign libraries, design issues, and FFI preprocessors.<br />
<br />
;[http://en.wikibooks.org/wiki/Programming:Haskell Programming Haskell Wikibook] <br />
:A communal effort by several authors to produce the definitive Haskell textbook. Its very much a work in progress at the moment, and contributions are welcome.<br />
<br />
;[http://video.s-inf.de/#FP.2005-SS-Giesl.(COt).HD_Videoaufzeichnung Video Lectures] <br />
:Lectures (in English) by Jürgen Giesl. About 30 hours in total, and great for learning Haskell. The lectures are 2005-SS-FP.V01 through 2005-SS-FP.V26. Videos 2005-SS-FP.U01 through 2005-SS-FP.U11 are exercise answer sessions, so you probably don't want those.<br />
<br />
;[http://www.cs.utoronto.ca/~trebla/fp/ Albert's Functional Programming Course] <br />
:A 15 lesson introduction to most aspects of Haskell.<br />
<br />
;[http://www.iceteks.com/articles.php/haskell/1 Introduction to Haskell]<br />
:By Chris Dutton, An "attempt to bring the ideas of functional programming to the masses here, and an experiment in finding ways to make it easy and interesting to follow".<br />
<br />
;[http://www.csc.depauw.edu/~bhoward/courses/0203Spring/csc122/haskintro/ An Introduction to Haskell]<br />
:A brief introduction, by Brian Howard.<br />
<br />
;[http://web.syntaxpolice.org/lectures/haskellTalk/slides/index.html Introduction to Haskell]<br />
:By Isaac Jones (2003).<br />
<br />
==Reference material==<br />
<br />
;[http://www.cs.uu.nl/~afie/haskell/tourofsyntax.html Tour of the Haskell Syntax] <br />
:By Arjan van IJzendoorn.<br />
<br />
;[http://zvon.org/other/haskell/Outputglobal/index.html Haskell Reference] <br />
:By Miloslav Nic.<br />
<br />
;[http://www.cs.uu.nl/~afie/haskell/tourofprelude.html A Tour of the Haskell Prelude] <br />
:By Bernie Pope and Arjan van IJzendoorn.<br />
<br />
;[http://members.chello.nl/hjgtuyl/tourdemonad.html A tour of the Haskell Monad functions]<br />
:By Henk-Jan van Tuyl.<br />
<br />
;[http://www.cse.unsw.edu.au/~en1000/haskell/inbuilt.html Useful Haskell functions]<br />
:An explanation for beginners of many Haskell functions that are predefined in the Haskell Prelude.<br />
<br />
;[http://haskell.org/ghc/docs/latest/html/libraries/ Documentation for the standard libraries]<br />
:Complete documentation of the standard Haskell libraries.<br />
<br />
;[http://www.haskell.org/haskellwiki/Category:Idioms Haskell idioms]<br />
:A collection of articles describing some common Haskell idioms. Often quite advanced.<br />
<br />
;[http://www.haskell.org/haskellwiki/Blow_your_mind Useful idioms]<br />
:A collection of short, useful Haskell idioms.<br />
<br />
;[http://www.haskell.org/haskellwiki/Programming_guidelines Programming guidelines]<br />
:Some Haskell programming and style conventions.<br />
<br />
== Motivation for Using Haskell ==<br />
<br />
;[http://www.md.chalmers.se/~rjmh/Papers/whyfp.html Why Functional Programming Matters] <br />
:By [http://www.md.chalmers.se/~rjmh/ John Hughes], The Computer Journal, Vol. 32, No. 2, 1989, pp. 98 - 107. Also in: David A. Turner (ed.): Research Topics in Functional Programming, Addison-Wesley, 1990, pp. 17 - 42.<BR> Exposes the advantages of functional programming languages. Demonstrates how higher-order functions and lazy evaluation enable new forms of modularization of programs.<br />
<br />
;[[Why Haskell matters]] <br />
:Discussion of the advantages of using Haskell in particular. An excellent article.<br />
<br />
;[http://www.cs.ukc.ac.uk/pubs/1997/224/index.html Higher-order + Polymorphic = Reusable] <br />
:By [http://www.cs.ukc.ac.uk/people/staff/sjt/index.html Simon Thompson]. Unpublished, May 1997.<BR> <STRONG>Abstract:</STRONG> This paper explores how certain ideas in object oriented languages have their correspondents in functional languages. In particular we look at the analogue of the iterators of the C++ standard template library. We also give an example of the use of constructor classes which feature in Haskell 1.3 and Gofer.<br />
<br />
==Analysis and Design Methods==<br />
<br />
See [[Analysis and design]].<br />
<br />
== Teaching Haskell == <br />
<br />
;[http://www.cs.ukc.ac.uk/pubs/1997/208/index.html Where do I begin? A problem solving approach to teaching functional programming]<br />
:By [http://www.cs.ukc.ac.uk/people/staff/sjt/index.html Simon Thompson]. In Krzysztof Apt, Pieter Hartel, and Paul Klint, editors, First International Conference on Declarative Programming Languages in Education. Springer-Verlag, September 1997. <br> <STRONG>Abstract:</STRONG> This paper introduces a problem solving method for teaching functional programming, based on Polya's `How To Solve It', an introductory investigation of mathematical method. We first present the language independent version, and then show in particular how it applies to the development of programs in Haskell. The method is illustrated by a sequence of examples and a larger case study. <br />
<br />
;[http://www.cs.ukc.ac.uk/pubs/1995/214/index.html Functional programming through the curriculum]<br />
:By [http://www.cs.ukc.ac.uk/people/staff/sjt/index.html Simon Thompson]and Steve Hill. In Pieter H. Hartel and Rinus Plasmeijer, editors, Functional Programming Languages in Education, LNCS 1022, pages 85-102. Springer-Verlag, December 1995. <br> <STRONG>Abstract:</STRONG> This paper discusses our experience in using a functional language in topics across the computer science curriculum. After examining the arguments for taking a functional approach, we look in detail at four case studies from different areas: programming language semantics, machine architectures, graphics and formal languages. <br />
<br />
;[http://www.cse.unsw.edu.au/~chak/papers/CK02a.html The Risks and Benefits of Teaching Purely Functional Programming in First Year]<br />
:By [http://www.cse.unsw.edu.au/~chak Manuel M. T. Chakravarty] and [http://www.cse.unsw.edu.au/~keller Gabriele Keller]. Journal of Functional Programming 14(1), pp 113-123, 2004. An earlier version of this paper was presented at Functional and Declarative Programming in Education (FDPE02). <br> <strong>Abstract</strong> We argue that teaching purely functional programming as such in freshman courses is detrimental to both the curriculum as well as to promoting the paradigm. Instead, we need to focus on the more general aims of teaching elementary techniques of programming and essential concepts of computing. We support this viewpoint with experience gained during several semesters of teaching large first-year classes (up to 600 students) in Haskell. These classes consisted of computer science students as well as students from other disciplines. We have systematically gathered student feedback by conducting surveys after each semester. This article contributes an approach to the use of modern functional languages in first year courses and, based on this, advocates the use of functional languages in this setting.<br />
<br />
==Using Monads==<br />
<br />
;[http://research.microsoft.com/%7Esimonpj/Papers/marktoberdorf Tackling the awkward squad: monadic input/output, concurrency, exceptions, and foreign-language calls in Haskell]<br />
:Simon Peyton Jones. Presented at the 2000 Marktoberdorf Summer School. In "Engineering theories of software construction", ed Tony Hoare, Manfred Broy, Ralf Steinbruggen, IOS Press, ISBN 1 58603 1724, 2001, pp47-96. The standard reference for monadic IO in GHC/Haskell. <br><strong>Abstract:</strong>Functional programming may be beautiful, but to write real applications we must grapple with awkward real-world issues: input/output, robustness, concurrency, and interfacing to programs written in other languages.<br />
<br />
;[http://db.ewi.utwente.nl/Publications/PaperStore/db-utwente-0000003696.pdf The Haskell Programmer's Guide to the IO Monad - Don't Panic.] <br />
:Stefan Klinger. This report scratches the surface of category theory, an abstract branch of algebra, just deep enough to find the monad structure. It seems well written.<br />
<br />
;[http://www.nomaware.com/monads/html/ All About Monads] <br />
:By Jeff Newbern. This tutorial aims to explain the concept of a monad and its application to functional programming in a way that is easy to understand and useful to beginning and intermediate Haskell programmers. Familiarity with the Haskell language is assumed, but no prior experience with monads is required. <br />
<br />
;[http://www.dcs.gla.ac.uk/~nww/Monad.html What the hell are Monads?] <br />
:By Noel Winstanley. A basic introduction to monads, monadic programming and IO. This introduction is presented by means of examples rather than theory, and assumes a little knowledge of Haskell. <br />
<br />
;[http://www.engr.mun.ca/~theo/Misc/haskell_and_monads.htm Monads for the Working Haskell Programmer -- a short tutorial]<br />
:By Theodore Norvell. <br />
<br />
See also [[Research papers/Monads and arrows]]<br />
<br />
==Using Arrows==<br />
<br />
;[http://www.haskell.org/arrows/ Arrows: A General Interface to Computation]<br />
:Ross Paterson's page on arrows.<br />
<br />
;[http://haskell.org/hawiki/UnderstandingArrows UnderstandingArrows]<br />
<br />
See also [[Research papers/Monads and arrows]]<br />
<br />
== Attribute Grammars ==<br />
<br />
Wouter Swierstra's [http://www.haskell.org/tmrwiki/WhyAttributeGrammarsMatter WhyAttributeGrammarsMatter].<br />
<br />
Utrecht University's [http://www.cs.uu.nl/wiki/HUT/AttributeGrammarSystem Attribute Grammar System] tools include also an attribute grammar compiler, UUAGC. The concept of attribute grammar was used in their [http://www.cs.uu.nl/wiki/Ehc/WebHome Essential Haskell Compiler] project, which gives us not only a working programming language, but also a good didactical material about using attribute grammars, e.g. in writing compilers.<br />
<br />
Albeits these materials are self-contained, they reveal that the theory of attribute grammars is related to these concepts:<br />
* circular programming<br />
* catamorphism<br />
Here is a HaWiki page on [http://haskell.org/hawiki/CircularProgramming CircularProgramming].<br />
<br />
== Categorical Programming ==<br />
<br />
Catamorphisms and related concepts, categorical approach to functional programming, categorical programming. Many materials cited here refer to category theory, so as an introduction to this discipline see the ''Foundations'' section at the end of this page.<br />
* Erik Meijer, Maarten Fokkinga, Ross Paterson: [http://citeseer.ist.psu.edu/meijer91functional.html Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire]. See also related documents (in the CiteSeer page). Understanding the article does not require a category theory knowledge -- a self-contained material on the concept of catamorphism, anamoprhism and other related concepts.<br />
* Varmo Vene and Tarmo Uustalu: [http://citeseer.ist.psu.edu/vene98functional.html Functional Programming with Apomorphisms / Corecursion]<br />
* Varmo Vene: [http://www.cs.ut.ee/~varmo/papers/thesis.pdf Categorical Programming with Inductive and Coinductive Types]. The book accompanies the deep categorical theory topic with Haskell examples.<br />
* Tatsuya Hagino: [http://www.tom.sfc.keio.ac.jp/~hagino/thesis.pdf A Categorical Programming Language]<br />
* [http://pll.cpsc.ucalgary.ca/charity1/www/home.html Charity], a categorical programming language implementation.<br />
* [http://okmij.org/ftp/Haskell/categorical-maxn.lhs Deeply uncurried products, as categorists might like them] article mentions a conjecture: relatedness to [[Combinatory logic]]<br />
<br />
== Data Structures ==<br />
<br />
;[http://www.cambridge.org/uk/catalogue/catalogue.asp?isbn=0521663504 Purely Functional Data Structures]<br />
:[http://www.cs.columbia.edu/~cdo/ Chris Okasaki], 232 pp., Cambridge University Press, 1998. ISBN 0-521-63124-6<BR> From the cover: <BLOCKQUOTE> Most books on data structures assume an imperative language like C or C++. However, data structures for these languages do not always translate well to functional languages such as Standard ML, Haskell, or Scheme. This book describes data structures and data structure design techniques from the point of view of functional languages. It includes code for a wide assortment both of classical data structures and of data structures developed exclusively for functional languages.This handy reference for professional programmers working with functional languages can also be used as a tutorial or for self-study. [http://www.cs.columbia.edu/~cdo/pfds-haskell.tar.gz Haskell source code for the book] </BLOCKQUOTE><br />
<br />
See [[Research papers/Data structures]]<br />
<br />
==Schools on Advanced Funtional Programming==<br />
<br />
<EM>Advanced Functional Programming</EM>, First International Spring<br />
School on Advanced Functional Programming Techniques, Bastad, Sweden, LNCS 925, Springer-Verlag, 1995 (editors: J. Jeuring, E. Meijer).<br />
*<EM>Functional Parsers</EM> by Jeroen Fokker, p.&nbsp;1-23.<br />
*<EM>Monads for functional programming</EM> by Philip Wadler, p.&nbsp;24-52.<br />
*<EM>The Design of a Pretty-printing Library</EM> by John Hughes, p.&nbsp;52-96.<br />
*<EM>Functional Programming with Overloading and Higher-Order Polymorphism</EM>, Mark P. Jones, p.&nbsp;97-136.<br />
*<EM>Programming with Fudgets</EM> by Thomas Hallgren and Magnus Carlsson, p.&nbsp;137-182.<br />
*<EM>Constructing Medium Sized Efficient Functional Programs in Clean</EM> by Marko C.J.D. van Eekelen and Rinus J. Plasmeijer, p.&nbsp;183-227.<br />
*<EM>Merging Monads and Folds for Functional Programming</EM> by Erik Meijer and Johan Jeuring, p.&nbsp;228-266.<br />
*<EM>Programming with Algebras</EM> by Richard B. Kieburtz and Jeffrey Lewis, p.&nbsp;267-307.<br />
*<EM>Graph Algorithms with a Functional Flavour</EM> by John Launchbury, p.&nbsp;308-331.<br />
<br />
[http://www.cse.ogi.edu/PacSoft/conf/summerschool96.html <EM>Advanced Functional Programming</EM>], Second International Summer School on Advanced Functional Programming Techniques, Evergreen State College, WA, USA, LNCS 1126, Springer-Verlag, 1996 (editors: J. Launchbury, E. Meijer, T. Sheard).<br />
*<EM>Composing the User Interface with Haggis</EM> by Sigbjorn Finne and Simon Peyton Jones, p.&nbsp;1-37.<br />
*<EM>Haskore Music Tutorial</EM> by Paul Hudak, p.&nbsp;38-67.<br />
*<EM>Polytypic Programming</EM> by Johan Jeuring and Patrick Jansson, p.&nbsp;68-114.<br />
*<EM>Implementing Threads in Standard ML</EM> by Peter Lee, p.&nbsp;115-130.<br />
*<EM>Functional Data Structures</EM> by Chris Okasaki, p.&nbsp;131-158.<br />
*<EM>Heap Profiling for Space Efficiency</EM> by Colin Runciman and Niklas R&ouml;jemo, p.&nbsp;159-183.<br />
*<EM&gt;Deterministic, Error-Correcting Combinator Parsers</EM> by S. Doaitse Swierstra and Luc Duponcheel, p.&nbsp;184-207.<br />
*<EM>Essentials of Standard ML Modules</EM> by Mads Tofte, p.&nbsp;208-238.<br />
<br />
[http://alfa.di.uminho.pt/~afp98/ Advanced Functional Programming, Third International School, AFP'98], <br />
in Braga, Portugal from 12th to 19th September 1998, LNCS 1608, Springer-Verlag, 1999<br />
(editors: D. Swierstra, P. Henriques and J. Oliveira).<BR><br />
All lecture notes and further material are available from the web site.<br />
<br />
= Foundations =<br />
<br />
;[http://www.dcs.qmul.ac.uk/~pt/Practical_Foundations/ Practical Foundations of Mathematics]<br />
:Paul Taylor. Cambridge University Press, ISBN: 0-521-63107-6, xii+576 pages, September 2000.<br />
<br />
;[http://www.cwru.edu/artsci/math/wells/pub/ttt.html Toposes, Triples and Theories]<br />
:Michael Barr and Charles Wells. The revised version of their formerly Springer Verlag published book is online for free download. Note that they use the name ''triple'' instead of ''monad''.<br />
<br />
=[[Research papers]]=<br />
<br />
A large collection of research papers published on various aspects of Haskell.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Hitchhikers_guide_to_Haskell&diff=3896Hitchhikers guide to Haskell2006-04-29T19:06:58Z<p>HenkJanVanTuyl: </p>
<hr />
<div>== Preface: DON'T PANIC! ==<br />
<br />
Recent experiences from a few of my fellow C++/Java programmers<br />
indicate that they read various Haskell tutorials with "exponential<br />
speedup" (think about how TCP/IP session starts up). They start slow<br />
and cautious, but when they see that the first 3-5 pages do not<br />
contain "anything interesting" in terms of code and examples, they<br />
begin skipping paragraphs, then chapters, then whole pages, only to<br />
slow down - often to a complete halt - somewhere on page 50, finding<br />
themselves in the thick of concepts like "type classes", "type<br />
constructors", "monadic IO", at which point they usually panic, think<br />
of a perfectly rational excuse not to read further anymore, and<br />
happily forget this sad and scary encounter with Haskell (as human<br />
beings usually tend to forget sad and scary things).<br />
<br />
This text intends to introduce the reader to the practical aspects of Haskell<br />
from the very beginning (plans for the first chapters include: I/O, darcs,<br />
Parsec, QuickCheck, profiling and debugging, to mention a few). The reader<br />
is expected to know (where to find) at least the basics of haskell: how to run<br />
"hugs" or "ghci", that layout is 2-dimensional, etc. Other than that, we do<br />
not plan to take radical leaps, and will go one step at a time in order not to<br />
lose the reader along the way. So DON'T PANIC, take your towel with you and<br />
read along.<br />
<br />
Oh, almost forgot: author is very interested in ANY feedback. Drop him a line<br />
or a word (see [[User:Adept|Adept]] for contact info) or submit<br />
patches to the tutorial via darcs (<br />
[http://adept.linux.kiev.ua/repos/hhgtth/ repository is here]) or directly to this<br />
Wiki. <br />
<br />
== Chapter 1: Ubiquitous "Hello world!" and other ways to do IO in Haskell ==<br />
<br />
Each chapter will be dedicated to one small real-life task which we will<br />
complete from the ground up.<br />
<br />
So here is the task for this chapter: in order to free up space on<br />
your hard drive for all the haskell code you are going to write in the<br />
nearest future, you are going to archive some of the old and dusty<br />
information on CDs and DVDs. While CD (or DVD) burning itself is easy<br />
these days, it usually takes some (or quite a lot ot) time to decide<br />
how to put several GB of digital photos on CD-Rs, when directories<br />
with images range from 10 to 300 Mb's in size, and you don't want to<br />
burn half-full (or half-empty) CD-Rs.<br />
<br />
So, the task is to write a program which will help us put a given<br />
collection of directories on the minimum possible amount of media,<br />
while packing the media as tightly as possible. Let's name this program<br />
"cd-fit".<br />
<br />
Oh. Wait. Let's do the usual "hello world" thing, before we forget about it,<br />
and then move on to more interesting things:<br />
<br />
-- put this in hello.hs<br />
module Main where<br />
main = putStrLn "Hello world!"<br />
<br />
Run it:<br />
<br />
$ runhaskell ./hello.hs<br />
Hello world!<br />
<br />
OK, we've done it. Move along now, nothing interesting here :)<br />
<br />
Any serious development must be done with the help of a version control<br />
system, and we will not make an exception. We will use the modern<br />
distributed version control system "darcs". "Modern" means that it is<br />
written in Haskell, "distributed" means that each working copy is<br />
a repository in itself.<br />
<br />
First, let's create an empty directory for all our code, and invoke<br />
"darcs init" there, which will create subdirectory "_darcs" to store<br />
all version-control-related stuff there.<br />
<br />
Fire up your favorite editor and create a new file called "cd-fit.hs"<br />
in our working directory. Now let's think for a moment about how our<br />
program will operate and express it in pseudocode:<br />
<br />
main = read list of directories and their sizes<br />
decide how to fit them on CD-Rs<br />
print solution<br />
<br />
Sounds reasonable? I thought so.<br />
<br />
Let's simplify our life a little and assume for now that we will<br />
compute directory sizes somewhere outside our program (for example,<br />
with "du -sb *") and read this information from stdin.<br />
Now let me convert all this to Haskell:<br />
<br />
module Main where<br />
<br />
main = do input <- getContents<br />
putStrLn ("DEBUG: got input " ++ input)<br />
-- compute solution and print it<br />
<br />
Not really working, but pretty close to plain English, eh? Let's stop<br />
for a moment and look more closely at what's written here line-by-line<br />
<br />
Let's begin from the top:<br />
<br />
input <- getContents<br />
<br />
This is an example of the Haskell syntax for doing IO (namely, input). This<br />
line is an instruction to read all the information available from the stdin,<br />
return it as a single string, and bind it to the symbol "input", so we can<br />
process this string any way we want.<br />
<br />
How did I know that? Did I memorize all the functions by heart? Of course not!<br />
Each function has a type, which, along with function's name, usually tells a<br />
lot about what a function will do.<br />
<br />
Let's fire up an interactive Haskell environment and examine this function<br />
up close:<br />
<br />
$ ghci<br />
___ ___ _<br />
/ _ \ /\ /\/ __(_)<br />
/ /_\// /_/ / / | | GHC Interactive, version 6.4.1, for Haskell 98.<br />
/ /_\\/ __ / /___| | http://www.haskell.org/ghc/<br />
\____/\/ /_/\____/|_| Type :? for help.<br />
<br />
Loading package base-1.0 ... linking ... done.<br />
Prelude> :type getContents<br />
getContents :: IO String<br />
Prelude> <br />
<br />
We see that "getContents" is a function without arguments, that will return<br />
"IO String". Prefix "IO" meant that this is an IO action. It will return<br />
String, when evaluated. Action will be evaluated as soon as we use "<-" to<br />
bind its result to some symbol.<br />
<br />
Note that "<-" is not a fancy way to assign value to variable. It is a way to<br />
evaluate (execute) IO actions, in other words - to actually do some I/O and<br />
return its result (if any). <br />
<br />
We can choose not to evaluate the action obtained from "getContents", but rather carry it around a bit and evaluate later:<br />
<br />
let x = getContents<br />
-- 300 lines of code here<br />
input <- x<br />
<br />
So, as you see, IO actions can act like an ordinary values. Suppose that we<br />
have built a list of IO actions and have found a way to execute them one by one.<br />
This would be a way to simulate imperative programming with its notion of<br />
"order of execution".<br />
<br />
Haskell allows you to do better than that. <br />
<br />
The standard language library (named "Prelude", by the way) provides<br />
us with lots of functions that return useful primitive IO actions. In<br />
order to combine them to produce an even more complex actions, we use a "do":<br />
<br />
c = do a <- someAction<br />
b <- someOtherAction<br />
print (bar b)<br />
print (foo a)<br />
putStrLn "done"<br />
<br />
Here we '''bind''' "c" to an action with the following "scenario":<br />
* '''evaluate''' action "someAction" and '''bind''' its result to "a"<br />
* then, '''evaluate''' "someOtherAction" and '''bind''' its result to "b"<br />
* then, process "b" with function "bar" and print result<br />
* then, process "a" with function "foo" and print result<br />
* then, print the word "done"<br />
<br />
When will all this actually be executed? Answer: as soon as we evaluate "c"<br />
using the "<-" (if it returns result, as "getContents" does) or just<br />
by using it as a function name (if it does not return a result, as "print"<br />
does):<br />
<br />
process = do putStrLn "Will do some processing"<br />
c<br />
putStrLn "Done"<br />
<br />
Notice that we took a bunch of functions ("someAction", "someOtherAction",<br />
"print", "putStrLn") and using "do" created from them a new function, which we<br />
bound to symbol "c". Now we could use "c" as a building block to produce an even<br />
more complex function, "process", and we could carry this on and on.<br />
Eventually, some of the functions will be mentioned in the code of function<br />
"main", to which the ultimate topmost IO action any Haskell program is bound.<br />
<br />
When will the "main" be executed/evaluated/forced? As soon as we run the<br />
program. Read this twice and try to comprehend: <br />
<br />
''The execution of a Haskell program is an evaluation of the symbol "main" to<br />
which we have bound an IO action. Via evaluation we obtain the result of that<br />
action''. <br />
<br />
Readers familiar with advanced C++ or Java programming and that arcane body of<br />
knowledge named "OOP Design Patterns" might note that "build actions from<br />
actions" and "evaluate actions to get result" is essentially a "Command<br />
pattern" and "Composition pattern" combined. Good news: in Haskell you get them<br />
for all your IO, and get them '''for free''' :)<br />
<br />
----<br />
'''Exercise:'''<br />
Consider the following code:<br />
<br />
module Main where<br />
c = putStrLn "C!"<br />
<br />
combine before after =<br />
do before<br />
putStrLn "In the middle"<br />
after<br />
<br />
main = do combine c c<br />
let b = combine (putStrLn "Hello!") (putStrLn "Bye!")<br />
let d = combine (b) (combine c c)<br />
putStrLn "So long!"<br />
<br />
See how we construct code out of thin air? Try to imagine what this code will<br />
do, then run it and check yourself. <br />
<br />
Do you understand why "Hello!" and "Bye!" are not printed?<br />
----<br />
<br />
Let's examine our "main" function closer:<br />
<br />
Prelude> :load cd-fit.hs<br />
Compiling Main ( ./cd-fit.hs, interpreted )<br />
Ok, modules loaded: Main.<br />
*Main> :type main<br />
main :: IO ()<br />
*Main> <br />
<br />
We see that "main" is indeed an IO action which will return nothing<br />
when evaluated. When combining actions with "do", the type of the<br />
result will be the type of the last action, and "putStrLn something" has type<br />
"IO ()": <br />
<br />
*Main> :type putStrLn "Hello world!"<br />
putStrLn "Hello world!" :: IO ()<br />
*Main> <br />
<br />
Oh, by the way: have you noticed that we actually compiled our first<br />
Haskell program in order to examine "main"? :)<br />
<br />
let's celebrate that by putting it under version control: execute<br />
"darcs add cd-fit.hs" and "darcs record", answer "y" to all questions<br />
and provide a commit comment "Skeleton of cd-fit.hs"<br />
<br />
Let's try to run it:<br />
<br />
$ echo "foo" | runhaskell cd-fit.hs<br />
DEBUG: got input foo<br />
<br />
----<br />
'''Exercises''':<br />
<br />
* Try to write a program that takes your name from the stdin and greets you (keywords: getLine, putStrLn);<br />
<br />
* Try to write a program that asks for you name, reads it, greets you, asks for your favorite color, and prints it back (keywords: getLine, putStrLn).<br />
<br />
== Chapter 2: Parsing the input ==<br />
<br />
OK, now that we have proper understanding of the powers of Haskell IO<br />
(and are awed by them, I hope), let's forget about IO and actually do<br />
some useful work. <br />
<br />
As you remember, we set forth to pack some CD-Rs as tightly as<br />
possible with data scattered in several input directories. We assume<br />
that "du -sb" will compute the sizes of input directories and output<br />
something like:<br />
<br />
65572 /home/adept/photos/raw-to-burn/dir1<br />
68268 /home/adept/photos/raw-to-burn/dir2<br />
53372 /home/adept/photos/raw-to-burn/dir3<br />
713124 /home/adept/photos/raw-to-burn/dir4<br />
437952 /home/adept/photos/raw-to-burn/dir5<br />
<br />
Our next task is to parse that input into some suitable internal<br />
representation.<br />
<br />
For that we will use powerful library of '''parsing combinators''' named<br />
"[[Parsec]]" which ships with most Haskell implementations.<br />
<br />
Much like the IO facilities we have seen in the first chapter, this<br />
library provides a set of basic parsers and means to combine into more<br />
complex parsing constructs.<br />
<br />
Unlike other tools in this area (lex/yacc or JavaCC to name a few),<br />
[[Parsec]] parsers do not require a separate preprocessing stage. Since in<br />
Haskell we can return function as a result of function and thus<br />
construct functions "from the thin air", there is no need for a separate<br />
syntax for parser description. But enough advertisements, let's actually<br />
do some parsing:<br />
<br />
import Text.ParserCombinators.Parsec<br />
<br />
-- parseInput parses output of "du -sb", which consists of many lines,<br />
-- each of which describes single directory<br />
parseInput = <br />
do dirs <- many dirAndSize<br />
eof<br />
return dirs<br />
<br />
-- Datatype Dir holds information about single directory - its size and name<br />
data Dir = Dir Int String deriving Show<br />
<br />
-- `dirAndSize` parses information about single directory, which is:<br />
-- a size in bytes (number), some spaces, then directory name, which extends till newline<br />
dirAndSize = <br />
do size <- many1 digit<br />
spaces<br />
dir_name <- anyChar `manyTill` newline<br />
return (Dir (read size) dir_name)<br />
<br />
Just add those lines to the top of "cd-fit.hs". Here we see quite a lot of new<br />
things, and several those that we know already. <br />
<br />
First of all, note the familiar "do" construct, which, as we know, is<br />
used to combine IO actions to produce new IO actions. Here we use it<br />
to combine "parsing" actions into new "parsing" actions. Does this<br />
mean that "parsing" implies "doing IO"? Not at all. Thing is, I must<br />
admit that I lied to you - "do" is used not only to combine IO<br />
actions. "Do" is used to combine any kind of so-called ''monadic<br />
actions'' or ''monadic values'' together.<br />
<br />
Think about [[monad]] as a "[[:Category:Idioms|design pattern]]" in the functional world.<br />
[[Monad]] is a way to hide from the user (programmer) all the machinery<br />
required for complex functionality to operate.<br />
<br />
As you might have heard, Haskell has no notion of "assignment",<br />
"mutable state", "variables", and is a "pure functional language",<br />
which means that every function called with the same input parameters<br />
will return exactly the same result. Meanwhile "doing IO" requires<br />
hauling around file handles and their states and dealing with IO<br />
errors. "Parsing" requires to track position in the input and dealing<br />
with parsing errors.<br />
<br />
In both cases Wise Men Who Wrote Libraries cared for our needs and<br />
hide all underlying complexities from us, exposing the [http://en.wikipedia.org/wiki/Application_programming_interface API] of their<br />
libraries (IO and parsing) in the form of "monadic action" which we<br />
are free to combine as we see fit. <br />
<br />
Think of programming with monads as of doing the remodelling with the<br />
help of professional remodelling crew. You describe sequence of<br />
actions on the piece of paper (that's us writing in "do" notation),<br />
and then, when required, that sequence will be evaluated by the<br />
remodelling crew ("in the monad") which will provide you with end<br />
result, hiding all the underlying complexity (how to prepare the<br />
paint, which nails to choose, etc) from you.<br />
<br />
let's use the interactive Haskell environment to decipher all the<br />
instructions we've written for the parsing library. As usually, we'll<br />
go top-down:<br />
<br />
*Main> :reload<br />
Ok, modules loaded: Main.<br />
*Main> :t parseInput<br />
parseInput :: GenParser Char st [Dir]<br />
*Main> :t dirAndSize<br />
dirAndSize :: GenParser Char st Dir<br />
*Main> <br />
<br />
Assuming (well, take my word for it) that "GenParser Char st" is our<br />
parsing monad, we could see that "parseInput", when evaluated, will<br />
produce a list of "Dir", and "dirAndSize", when evaluated, will<br />
produce "Dir". Assuming that "Dir" somehow represents information<br />
about single directory, that is pretty much what we wanted, isn't it?<br />
<br />
Let's see what a "Dir" means. We defined ''data[[type]]'' Dir as a record,<br />
which holds an Int and a String:<br />
<br />
data Dir = Dir Int String deriving Show<br />
<br />
In order to construct such records, we must use ''data [[constructor]]''<br />
Dir:<br />
<br />
*Main> :t Dir 1 "foo"<br />
Dir 1 "foo" :: Dir<br />
<br />
In order to reduce confusion for newbies, we could have written:<br />
<br />
data Dir = D Int String deriving Show<br />
<br />
, which would define ''data[[type]]'' "Dir" with ''data [[constructor]]'' "D".<br />
However, traditionally name of the data[[type]] and its [[constructor]] are<br />
chosen to be the same.<br />
<br />
Clause "[[deriving]] Show" instructs the compiler to make enough code "behind<br />
the curtains" to make this ''datatype'' conform to the interface of<br />
the ''type [[class]]'' Show. We will explain ''type [[class]]es'' later, for<br />
now let's just say that this will allow us to "print" instances of<br />
"Dir".<br />
<br />
Exercises: <br />
* examine types of "digit", "anyChar", "many", "many1" and "manyTill" to see how they are used to build more complex parsers from single ones.<br />
<br />
* compare types of "manyTill", "manyTill anyChar" and "manyTill anyChar newline". Note that "anyChar `manyTill` newline" is just another syntax sugar. Note that when function is supplied with less arguments that it actually needs, we get not a value, but a new function, which is called ''partial application''.<br />
<br />
<br />
OK. So, we combined a lot of primitive parsing actions to get ourselves a<br />
parser for output of "du -sb". How can we actually parse something? the [[Parsec]] library supplies us with function "parse":<br />
<br />
*Main> :t parse<br />
parse :: GenParser tok () a<br />
-> SourceName<br />
-> [tok]<br />
-> Either ParseError a<br />
*Main> :t parse parseInput<br />
parse parseInput :: SourceName -> [Char] -> Either ParseError [Dir]<br />
*Main> <br />
<br />
At first the [[type]] might be a bit cryptic, but once we supply "parse" with the parser we made, the compiler gets more information and presents us with a more concise [[type]].<br />
<br />
Stop and consider this for a moment. The compiler figured out type of the function without a single type annotation supplied by us! Imagine if a Java compiler deduced types for you, and you wouldn't have to specify types of arguments and return values of methods, ever.<br />
<br />
OK, back to the code. We can observe that the "parser" is a function, which,<br />
given a parser, a name of the source file or channel (f.e. "stdin"), and<br />
source data (String, which is a list of "Char"s, which is written "[Char]"),<br />
will either produce parse error, or parse us a list of "Dir".<br />
<br />
Datatype "Either" is an example of datatype whose constructor has name, different<br />
from the name of the datatype. In fact, "Either" has two constructors:<br />
<br />
data Either a b = Left a | Right b<br />
<br />
In order to undestand better what does this mean consider the following<br />
example:<br />
<br />
*Main> :t Left 'a'<br />
Left 'a' :: Either Char b<br />
*Main> :t Right "aaa"<br />
Right "aaa" :: Either a [Char]<br />
*Main> <br />
<br />
You see that "Either" is a ''union'' (much like the C/C++ "union") which could<br />
hold value of one of the two distinct types. However, unlike C/C++ "union",<br />
when presented with value of type "Either Int Char" we could immediately see<br />
whether its an Int or a Char - by looking at the constructor which was used to<br />
produce the value. Such datatypes are called "tagged unions", and they are<br />
another [[:Category:Idioms|power tool]] in the Haskell toolset.<br />
<br />
Did you also notice that we provide "parse" with parser, which is a monadic<br />
value, but receive not a new monadic value, but a parsing result? That is<br />
because "parse" is an evaluator for "Parser" monad, much like the [[GHC]] or [[Hugs]] runtime is an evaluator for the IO monad. The function "parser" implements all monadic machinery: it tracks errors and positions in input, implements backtracking and lookahead, etc.<br />
<br />
let's extend our "main" function to use "parse" and actually parse the input<br />
and show us the parsed data structures:<br />
<br />
main = do input <- getContents<br />
putStrLn ("DEBUG: got input " ++ input)<br />
let dirs = case parse parseInput "stdin" input of<br />
Left err -> error $ "Input:\n" ++ show input ++ <br />
"\nError:\n" ++ show err<br />
Right result -> result<br />
putStrLn "DEBUG: parsed:"; print dirs<br />
<br />
Exercise:<br />
<br />
* In order to understand this snippet of code better, examine (with ghci or hugs) the difference between 'drop 1 ( drop 1 ( drop 1 ( drop 1 ( drop 1 "foobar" ))))' and 'drop 1 $ drop 1 $ drop 1 $ drop 1 $ drop 1 "foobar"'. Examine type of ($).<br />
* Try putStrLn "aaa" and print "aaa" and see the difference, examine their types.<br />
* Try print (Dir 1 "foo") and putStrLn (Dir 1 "foo"). Examine types of print and putStrLn to understand the behavior in both cases.<br />
<br />
Let's try to run what we have so far:<br />
<br />
$ du -sb * | runhaskell ./cd-fit.hs<br />
<br />
DEBUG: got input 22325 Article.txt<br />
18928 Article.txt~<br />
1706 cd-fit.hs<br />
964 cd-fit.hs~<br />
61609 _darcs<br />
<br />
DEBUG: parsed:<br />
[Dir 22325 "Article.txt",Dir 18928 "Article.txt~",Dir 1706 "cd-fit.hs",Dir 964 "cd-fit.hs~",Dir 61609 "_darcs"]<br />
<br />
Seems to be doing exactly as planned. Now let's try some erroneous<br />
input:<br />
<br />
$ echo "foo" | runhaskell cd-fit.hs<br />
DEBUG: got input foo<br />
<br />
DEBUG: parsed:<br />
*** Exception: Input:<br />
"foo\n"<br />
Error:<br />
"stdin" (line 1, column 1):<br />
unexpected "f"<br />
expecting digit or end of input<br />
<br />
Seems to be doing fine. Let's "darcs record" it, giving the commit comment "Implemented parsing of input".<br />
<br />
----<br />
Here is complete "cd-fit.hs" what we should have written so far:<br />
<br />
module Main where<br />
<br />
import Text.ParserCombinators.Parsec<br />
<br />
-- Output of "du -sb" -- which is our input -- consists of many lines,<br />
-- each of which describes single directory<br />
parseInput = <br />
do dirs <- many dirAndSize<br />
eof<br />
return dirs<br />
<br />
-- Information about single direcory is a size (number), some spaces,<br />
-- then directory name, which extends till newline<br />
data Dir = Dir Int String deriving Show<br />
dirAndSize = <br />
do size <- many1 digit<br />
spaces<br />
dir_name <- anyChar `manyTill` newline<br />
return $ Dir (read size) dir_name<br />
<br />
main = do input <- getContents<br />
putStrLn ("DEBUG: got input " ++ input)<br />
let dirs = case parse parseInput "stdin" input of<br />
Left err -> error $ "Input:\n" ++ show input ++ <br />
"\nError:\n" ++ show err<br />
Right result -> result<br />
putStrLn "DEBUG: parsed:"; print dirs<br />
-- compute solution and print it<br />
<br />
<br />
== Chapter 3: Packing the knapsack and testing it with class, too (and don't forget your towel!) ==<br />
<br />
Enough preliminaries already. let's go pack some CDs.<br />
<br />
As you might already have recognized, our problem is a classical one. It is<br />
called a "knapsack problem" ([http://www.google.com/search?q=knapsack+problem google it up], if you don't know already what it<br />
is. There are more than 100000 links).<br />
<br />
let's start from the greedy solution, but first let's slightly modify our "Dir"<br />
datatype to allow easy extraction of its components:<br />
<br />
data Dir = Dir {dir_size::Int, dir_name::String} deriving Show<br />
<br />
----<br />
Exercise: examine types of "Dir", "dir_size" and "dir_name"<br />
----<br />
<br />
From now on, we could use "dir_size d" to get a size of directory, and<br />
"dir_name d" to get its name, provided that "d" is of type "Dir".<br />
<br />
The Greedy algorithm sorts directories from the biggest down, and tries to puts<br />
them on CD one by one, until there is no room for more. We will need to track<br />
which directories we added to CD, so let's add another datatype, and code this<br />
simple packing algorithm:<br />
<br />
import Data.List (sortBy)<br />
<br />
-- DirPack holds a set of directories which are to be stored on single CD.<br />
-- 'pack_size' could be calculated, but we will store it separately to reduce<br />
-- amount of calculation<br />
data DirPack = DirPack {pack_size::Int, dirs::[Dir]} deriving Show<br />
<br />
-- For simplicity, let's assume that we deal with standard 700 Mb CDs for now<br />
media_size = 700*1024*1024<br />
<br />
-- Greedy packer tries to add directories one by one to initially empty 'DirPack'<br />
greedy_pack dirs = foldl maybe_add_dir (DirPack 0 []) $ sortBy cmpSize dirs<br />
where<br />
cmpSize d1 d2 = compare (dir_size d1) (dir_size d2)<br />
<br />
-- Helper function, which only adds directory "d" to the pack "p" when new<br />
-- total size does not exceed media_size<br />
maybe_add_dir p d =<br />
let new_size = pack_size p + dir_size d<br />
new_dirs = d:(dirs p)<br />
in if new_size > media_size then p else DirPack new_size new_dirs<br />
<br />
----<br />
I'll highlight the areas which you could explore on your own (using other nice<br />
tutorials out there, of which I especially recommend "Yet Another Haskell<br />
Tutorial" by Hal Daume):<br />
* We choose to import a single function "sortBy" from a module [[Data.List]], not the whole thing.<br />
* Instead of coding case-by-case recursive definition of "greedy_pack", we go with higher-order approach, choosing "foldl" as a vehicle for list traversal. Examine its type. Other useful function from the same category are "map", "foldr", "scanl" and "scanr". Look them up!<br />
* To sort list of "Dir" by size only, we use custom sort function and parameterized sort - "sortBy". This sort of setup where the user may provide a custom "modifier" for a generic library function is quite common: look up "deleteBy", "deleteFirstsBy", "groupBy", "insertBy", "intersectBy", "maximumBy", "minimumBy", "sortBy", "unionBy".<br />
* To code the quite complex function "maybe_add_dir", we introduced several '''local definitions''' in the "let" clause, which we can reuse within the function body. We used a "where" clause in the "greedy_pack" function to achieve the same effect. Read about "let" and "where" clauses and the differences between them.<br />
* Note that in order to construct a new value of type "DirPack" (in function "maybe_add_dir") we haven't used the helper accessor functions "pack_size" and "dirs"<br />
----<br />
<br />
In order to actually use our greedy packer we must call it from our "main"<br />
function, so let's add a lines:<br />
<br />
main = do ...<br />
-- compute solution and print it<br />
putStrLn "Solution:" ; print (greedy_pack dirs)<br />
<br />
Verify integrity of our definitions by (re)loading our code in ghci. Compiles?<br />
Thought so :)<br />
<br />
Now it is time to test our creation. We could do it by actually running it in<br />
the wild like this:<br />
<br />
$ du -sb ~/DOWNLOADS/* | runhaskell ./cd-fit.hs<br />
<br />
This will prove that our code seems to be working. At least, this once. How<br />
about establishing with reasonable degree of certainty that our code, parts<br />
and the whole, works properly, and doing so in re-usable manner? In other<br />
words, how about writing some test?<br />
<br />
Java programmers used to JUnit probably thought about screens of boiler-plate<br />
code and hand-coded method invocations. Never fear, we will not do anything as<br />
silly :)<br />
<br />
Enter '''[[QuickCheck]]'''.<br />
<br />
[[QuickCheck]] is a tool to do automated testing of your functions using<br />
(semi)random input data. In the spirit of "100b of code examples is worth 1kb of<br />
praise" let's show the code for testing the following ''property'': An attempt to pack directories returned by "greedy_pack" should return "DirPack" of exactly the same pack:<br />
<br />
import Test.QuickCheck<br />
import Control.Monad (liftM2)<br />
<br />
-- We must teach QuickCheck how to generate arbitrary "Dir"s<br />
instance Arbitrary Dir where<br />
-- Let's just skip "coarbitrary" for now, ok? <br />
-- I promise, we will get back to it later :)<br />
coarbitrary = undefined<br />
-- We generate arbitrary "Dir" by generating random size and random name<br />
-- and stuffing them inside "Dir"<br />
arbitrary = liftM2 Dir gen_size gen_name<br />
-- Generate random size between 10 and 1400 Mb<br />
where gen_size = do s <- choose (10,1400)<br />
return (s*1024*1024)<br />
-- Generate random name 1 to 300 chars long, consisting of symbols "fubar/" <br />
gen_name = do n <- choose (1,300)<br />
sequence $ take (n*10+1) $ repeat (elements "fubar/")<br />
<br />
-- For convenience and by tradition, all QuickCheck tests begin with prefix "prop_".<br />
-- Assume that "ds" will be a random list of "Dir"s and code your test.<br />
prop_greedy_pack_is_fixpoint ds =<br />
let pack = greedy_pack ds <br />
in pack_size pack == pack_size (greedy_pack (dirs pack))<br />
<br />
let's run the test, after which I'll explain how it all works:<br />
<br />
Prelude> :r<br />
Compiling Main ( ./cd-fit.hs, interpreted )<br />
Ok, modules loaded: Main.<br />
*Main> quickCheck prop_greedy_pack_is_fixpoint<br />
[numbers spinning]<br />
OK, passed 100 tests.<br />
*Main> <br />
<br />
We've just seen our "greedy_pack" run on a 100 completely (well, almost<br />
completely) random lists of "Dir"s, and it seems that property indeed holds.<br />
<br />
let's dissect the code. The most intriguing part is "instance Arbitrary Dir<br />
where", which declares that "Dir" is an '''[[instance]]''' of '''type[[class]]''' "Arbitrary". Whoa, that's a whole lot of unknown words! :) Let's slow down a<br />
bit. <br />
<br />
What is a '''type[[class]]'''? A typeclass is a Haskell way of dealing with the<br />
following situation: suppose that you are writing a library of usefull<br />
functions and you dont know in advance how exactly they will be used, so you<br />
want to make them generic. Now, on one hand you dont want to restrict your<br />
users to certain type (e.g. String). On the other hand, you want to enforce<br />
the convention that arguments for your function must satisfy a certain set of<br />
constraints. That is where '''typeclass''' comes in handy. <br />
<br />
Think of typeclass as a '''contract''' (or "interface", in Java terms) that<br />
your type must fulfill in order to be admitted as an argument to certain<br />
functions. <br />
<br />
Let's examine the typeclass "Arbitrary":<br />
<br />
*Main> :i Arbitrary<br />
class Arbitrary a where<br />
arbitrary :: Gen a<br />
coarbitrary :: a -> Gen b -> Gen b<br />
-- Imported from Test.QuickCheck<br />
instance Arbitrary Dir<br />
-- Defined at ./cd-fit.hs:61:0<br />
instance Arbitrary Bool -- Imported from Test.QuickCheck<br />
instance Arbitrary Double -- Imported from Test.QuickCheck<br />
instance Arbitrary Float -- Imported from Test.QuickCheck<br />
instance Arbitrary Int -- Imported from Test.QuickCheck<br />
instance Arbitrary Integer -- Imported from Test.QuickCheck<br />
-- rest skipped --<br />
<br />
It could be read this way: "Any [[type]] (let's name it 'a') could be a member of the [[class]] Arbitrary as soon as we define two functions for it: "arbitrary" and "coarbitrary", with signatures shown. For types Dir, Bool, Double, Float, Int and Integer such definitions were provided, so all those types are instance of class Arbitrary".<br />
<br />
Now, if you write a function which operates on its arguments solely by means<br />
of "arbitrary" and "coarbitrary", you can be sure that this function will work<br />
on any type which is an instance of "Arbitrary"!<br />
<br />
let's say it again. Someone (maybe even you) writes the code (API or library),<br />
which requires that input values implement certain ''interfaces'', which is<br />
described in terms of functions. Once you show how your type implements this<br />
''interface'' you are free to use API or library.<br />
<br />
Conside the function "sort" from standard library:<br />
<br />
*Main> :t Data.List.sort<br />
Data.List.sort :: (Ord a) => [a] -> [a]<br />
<br />
We see that it sorts lists of any values which are instance of typeclass<br />
"Ord". Let's examine that class:<br />
<br />
*Main> :i Ord<br />
class Eq a => Ord a where<br />
compare :: a -> a -> Ordering<br />
(<) :: a -> a -> Bool<br />
(>=) :: a -> a -> Bool<br />
(>) :: a -> a -> Bool<br />
(<=) :: a -> a -> Bool<br />
max :: a -> a -> a<br />
min :: a -> a -> a<br />
-- skip<br />
instance Ord Double -- Imported from GHC.Float<br />
instance Ord Float -- Imported from GHC.Float<br />
instance Ord Bool -- Imported from GHC.Base<br />
instance Ord Char -- Imported from GHC.Base<br />
instance Ord Integer -- Imported from GHC.Num<br />
instance Ord Int -- Imported from GHC.Base<br />
-- skip<br />
*Main> <br />
<br />
We see a couple of interesting things: first, there is an additional<br />
requirement listed: in order to be an instance of "Ord", type must first be an<br />
instance of typeclass "Eq". Then, we see that there is an awful lot of<br />
functions to define in order to be an instance of "Ord". Wait a second, isn't<br />
it silly to define both (<) and (>) when one could be expressed via another? <br />
<br />
Right you are! Usually, typeclass contains several "default" implementation<br />
for its functions, when it is possible to express them through each other (as<br />
it is with "Ord"). In this case it is possible to supply only a minimal<br />
definition (which in case of "Ord" consists of any single function) and others<br />
will be automatically derived. If you supplied fewer functions than are required<br />
for minimal implementation, the compiler/interpreter will say so and<br />
explain which functions you still have to define.<br />
<br />
Once again, we see that a lot of [[type]]s are already instances of typeclass Ord, and thus we are able to sort them.<br />
<br />
Now, let's take a look back to the definition of "Dir":<br />
<br />
data Dir = Dir {dir_size::Int, dir_name::String} deriving Show<br />
<br />
See that "[[deriving]]" clause? It instructs the compiler to automatically derive code to make "Dir" an instance of typeclass Show. The compiler knows about a bunch of standard typeclasses (Eq, Ord, Show, Enum, Bound, Typeable to name a few) and knows how to make a type into a "suitably good" instance of any of them. If you want to derive instances of more than one typeclass, say it this way: "deriving (Eq,Ord,Show)". Voila! Now we can compare, sort and print data of<br />
that type!<br />
<br />
Sidenote for Java programmers: just imagine java compiler which derives code<br />
for "implements Storable" for you...<br />
<br />
Sidenote for C++ programmers: just imagine that deep copy constructors are<br />
being written for you by compiler....<br />
<br />
----<br />
Exercises:<br />
* Examine typeclasses Eq and Show<br />
* Examine types of (==) and "print"<br />
* Try to make "Dir" instance of "Eq"<br />
----<br />
<br />
OK, back to our tests. So, what we have had to do in order to make "Dir" an<br />
instance of "Arbitrary"? Minimal definition consists of "arbitrary". Let's<br />
examine it up close:<br />
<br />
*Main> :t arbitrary<br />
arbitrary :: (Arbitrary a) => Gen a<br />
<br />
See that "Gen a"? Reminds you of something? Right! Think of "IO a" and "Parser<br />
a" which we've seen already. This is yet another example of action-returning<br />
function, which could be used inside "do"-notation. (You might ask yourself,<br />
wouldn't it be useful to generalize that convenient concept of actions and<br />
"do"? Of course! It is already done, the concept is called "[[Monad]]" and we will talk about it in Chapter 400 :) )<br />
<br />
Since 'a' here is a [[type variable]] which is an instance of "Arbitrary", we could substitute "Dir" here. So, how we can make and return an action of type "Gen Dir"?<br />
<br />
Let's look at the code:<br />
<br />
arbitrary = liftM2 Dir gen_size gen_name<br />
-- Generate random size between 10 and 1400 Mb<br />
where gen_size = do s <- choose (10,1400)<br />
return (s*1024*1024)<br />
-- Generate random name 1 to 300 chars long, consisting of symbols "fubar/" <br />
gen_name = do n <- choose (1,300)<br />
sequence $ take (n*10+1) $ repeat (elements "fubar/")<br />
<br />
We have used the library-provided functions "choose" and "elements" to build up<br />
"gen_size :: Gen Int" and "gen_name :: Gen String" (exercise: don't take my<br />
word on that. Find a way to check types of "gen_name" and "gen_size"). Since<br />
"Int" and "String" are components of "Dir", we sure must be able to use "Gen<br />
Int" and "Gen String" to build "Gen Dir". But where is the "do" block for<br />
that? There is none, and there is only single call to "liftM2". <br />
<br />
Let's examine it:<br />
<br />
*Main> :t liftM2<br />
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r<br />
<br />
Kind of scary, right? Let's provide typechecker with more context:<br />
<br />
*Main> :t liftM2 Dir<br />
liftM2 Dir :: (Monad m) => m Int -> m String -> m Dir<br />
<br />
Since you already heard that "Gen" is a "Monad", you could substitute "Gen" for "m" here, obtaining "liftM2 Dir :: (Monad Gen) => Gen Int -> Gen String -><br />
Gen Dir". Exactly what we wanted!<br />
<br />
Consider "liftM2" to be "advanced topic" of this chapter (which we will cover<br />
later) and just note for now that:<br />
* "2" is a number of arguments for data constructor "Dir" and we have used "liftM2" to construct "Gen Dir" out of "Dir"<br />
* There are also "liftM", "liftM3", "liftM4", "liftM5"<br />
* "liftM2" is defined as "liftM2 f a1 a2 = do x<-a1; y<-a2; return (f x y)"<br />
<br />
Hopefully, this will all make sense after you read it for the third time ;)<br />
<br />
== Chapter 4: REALLY packing the knapsack this time == <br />
<br />
In this chapter we are going to write another not-so-trivial packing<br />
method, compare packing methods efficiency, and learn something new<br />
about debugging and profiling of the Haskell programs along the way.<br />
<br />
It might not be immediately obvious whether our packing algorith is<br />
effective, and if yes - in which particular way? Whether it's runtime,<br />
memory consumption or result are of sufficient quality, are there any<br />
alternative algorithms, and how do they compare to each other?<br />
<br />
Let's code another solution to the knapsack packing problem, called the "dynamic programming method" and put both variants to the test.<br />
<br />
This time, I'll not dissect the listing and explain it bit by bit. Instead, comments are provided in the code:<br />
<br />
----------------------------------------------------------------------------------<br />
-- Dynamic programming solution to the knapsack (or, rather, disk) packing problem<br />
--<br />
-- Let the `bestDisk x' be the "most tightly packed" disk of total <br />
-- size no more than `x'.<br />
precomputeDisksFor :: [Dir] -> [DirPack]<br />
precomputeDisksFor dirs = <br />
-- By calculating `bestDisk' for all possible disk sizes, we could<br />
-- obtain a solution for particular case by simple lookup in our list of<br />
-- solutions :)<br />
let precomp = map bestDisk [0..] <br />
<br />
-- How to calculate `bestDisk'? Lets opt for a recursive definition:<br />
-- Recursion base: best packed disk of size 0 is empty<br />
bestDisk 0 = DirPack 0 []<br />
-- Recursion step: for size `limit`, bigger than 0, best packed disk is<br />
-- comptued as follows:<br />
bestDisk limit = <br />
-- 1. Take all non-empty dirs that could possibly fit to that disk by itself.<br />
-- Consider them one by one. Let the size of particular dir be `dir_size d'.<br />
-- Let's add it to the best-packed disk of size <= (limit - dir_size d), thus<br />
-- producing the disk of size <= limit. Lets do that for all "candidate" dirs that<br />
-- are not yet on our disk:<br />
case [ DirPack (dir_size d + s) (d:ds) | d <- filter ( (inRange (1,limit)).dir_size ) dirs<br />
, dir_size d > 0<br />
, let (DirPack s ds)=precomp!!(limit - dir_size d)<br />
, d `notElem` ds<br />
] of<br />
-- We either fail to add any dirs (probably, because all of them too big).<br />
-- Well, just report that disk must be left empty:<br />
[] -> DirPack 0 []<br />
-- Or we produce some alternative packings. Let's choose the best of them all:<br />
packs -> maximumBy cmpSize packs<br />
<br />
cmpSize a b = compare (pack_size a) (pack_size b)<br />
<br />
in precomp<br />
<br />
-- When we precomputed disk of all possible sizes for the given set of dirs, solution to <br />
-- particular problem is simple: just take the solution for the required 'media_size' and<br />
-- that's it!<br />
dynamic_pack dirs = (precomputeDisksFor dirs)!!media_size<br />
<br />
Notice that it took almost the same amount of text to describe algorithm and to write implementation for it. Nice, eh?<br />
<br />
----<br />
<br />
Exercises:<br />
* Make all necessary amendments to the previously written code to make this example compile. Hints: browse modules Data.List and Data.Ix for functions that are "missing" - maybe you will find them there (use ":browse Module.Name" at ghci prompt). Have you had to define some new instances of some classes? How did you do that?<br />
* <tt>[ other_function local_binding | x <- some_list, x > 0, let local_binding = some_function x ]</tt> is called a "list comprehension". This is another example of "syntactic sugar", which could lead to nicely readable code, but, when abused, could lead to syntactic caries :) Do you understand what does this sample do: <tt>let solve x = [ y | x <- [0..], y<-[0..], y == x * x ]</tt>? Could write (with help of decent tutorial) write de-sugared version of this? (Yes, I know that finding a square root does not require list traversals, but for the sake of self-education try and do it)<br />
* Notice that in order to code quite complex implementation of <tt>precomputeDisksFor</tt> we split it up in several smaller pieces and put them as a '''local bindings''' inside '''let''' clause.<br />
* Notice that we use '''pattern matching''' to both define <tt>bestKnap</tt> on case-by-case basis and to "peer into" ('''de-construct''') <tt>DirPack</tt> in the <tt>let (DirPack s ds)=precomp!!(limit - dir_size d)</tt> line<br />
* Notice how we use function composition to compose complex condition to filter the list of dirs<br />
<br />
---- <br />
<br />
Now, lets code the QuickCheck test for this function along the lines of the test for <tt>greedy_pack</tt>:<br />
<br />
prop_dynamic_pack_is_fixpoint ds =<br />
let pack = dynamic_pack ds <br />
in pack_size pack == pack_size (dynamic_pack (dirs pack))<br />
<br />
Now, lets try to run (DONT PANIC and save all you work in other applications first!):<br />
<br />
*Main> quickCheck dynamic_pack_is_fixpoint<br />
<br />
Now, you took my advice seriously, dont you? And you did have your '''Ctrl-C''' handy, didn't you? Most probably, the attempt to run the test resulted in all your memory being taken by <tt>ghci</tt> process, which you hopefully interrupted soon enough by pressing '''Ctrl-C'''.<br />
<br />
What happened? Who ate all the memory? How to debug this problem? GHC comes with profiling abilities, but we could not use them - they produce report after program terminates, and our doesn't seem to do so without consuming several terabytes of memory first. Still, there is a lot of room for maneuver.<br />
<br />
Let's see. Since the have called <tt>dynamic_pack</tt> and it ate all the memory, let's not do this again. Instead, let's see what this function does and tweak it a bit to explore it's behavoir.<br />
<br />
Since we already know that random lists of "Dir"s generated for our QuickCheck tests are of modest size (after all, <tt>greedy_pack</tt> muches them without significant memory consumption), the size of the input most probably is not the issue. However, <tt>dynamic_pack_is_fixpoint</tt> is building quite a huge list internally (via <tt>precomputeDisksFor</tt>). Could this be a problem?<br />
<br />
Let's turn the timing/memory stats on (":set +s" on ghci prompt) and try to peek into various elements of list returned by <tt>precomputeDisksFor</tt>:<br />
<br />
Prelude> :l cd-fit.hs<br />
Compiling Main ( cd-fit.hs, interpreted )<br />
Ok, modules loaded: Main.<br />
*Main> :set +s<br />
*Main> (precomputeDisksFor [Dir 1 "aaa"]) !! 0<br />
DirPack {pack_size = 0, dirs = []}<br />
(0.06 secs, 1277972 bytes)<br />
*Main> (precomputeDisksFor [Dir 1 "aaa"]) !! 10<br />
DirPack {pack_size = 0, dirs = []}<br />
(0.00 secs, 0 bytes)<br />
*Main> (precomputeDisksFor [Dir 1 "aaa"]) !! 100<br />
DirPack {pack_size = 0, dirs = []}<br />
(0.01 secs, 1519064 bytes)<br />
*Main> (precomputeDisksFor [Dir 1 "aaa"]) !! 1000<br />
DirPack {pack_size = 0, dirs = []}<br />
(0.03 secs, 1081808 bytes)<br />
*Main> (precomputeDisksFor [Dir 1 "aaa"]) !! 10000<br />
DirPack {pack_size = 0, dirs = []}<br />
(1.39 secs, 12714088 bytes)<br />
*Main> (precomputeDisksFor [Dir 1 "aaa"]) !! 100000<br />
Interrupted.<br />
<br />
Aha! This seems to be a problem, since computation of 100000 fails to terminate in "reasonable" time, and to think that we have tried to compute <tt>700*1024*1024</tt>th element...<br />
<br />
Lets modify our code a bit, to allow disk size to be tweaked:<br />
<br />
dynamic_pack limit dirs = (precomputeDisksFor dirs)!!limit<br />
<br />
prop_dynamic_pack_is_fixpoint ds =<br />
let pack = dynamic_pack media_size ds <br />
in pack_size pack == pack_size (dynamic_pack media_size (dirs pack))<br />
<br />
prop_dynamic_pack_small_disk ds =<br />
let pack = dynamic_pack 50000 ds<br />
in pack_size pack == pack_size (dynamic_pack 50000 (dirs pack))<br />
<br />
-- rename "old" main to "moin"<br />
main = quickCheck prop_dynamic_pack_small_disk<br />
<br />
Compute a profiling version of you code with <tt>ghc -O --make -prof -auto-all -o cd-fit cd-fit.hs</tt> and run it like this: <br />
<br />
$ ./cd-fit +RTS -p<br />
OK, passed 100 tests.<br />
<br />
First thing, note that our code satisfies at least one simple property. Good. Now let's examine profile. Look into file "cd-fit.prof", which was produced in your current directory. <br />
<br />
Most probably, you'll see something like this:<br />
<br />
cd-fit +RTS -p -RTS<br />
<br />
total time = 2.18 secs (109 ticks @ 20 ms)<br />
total alloc = 721,433,008 bytes (excludes profiling overheads)<br />
<br />
COST CENTRE MODULE %time %alloc<br />
<br />
precomputeDisksFor Main 88.1 99.8<br />
dynamic_pack Main 11.0 0.0<br />
individual inherited<br />
COST CENTRE MODULE no. entries %time %alloc %time %alloc<br />
<br />
MAIN MAIN 1 0 0.0 0.0 100.0 100.0<br />
CAF Main 174 11 0.9 0.2 100.0 100.0<br />
prop_dynamic_pack_small_disk Main 181 100 0.0 0.0 99.1 99.8<br />
dynamic_pack Main 182 200 11.0 0.0 99.1 99.8<br />
precomputeDisksFor Main 183 200 88.1 99.8 88.1 99.8<br />
main Main 180 1 0.0 0.0 0.0 0.0<br />
<br />
Examine column of "individual %alloc". As we thought, all memory was<br />
allocated within <tt>precomputeDisksFor</tt>. However, amount of<br />
memory allocated (more than 700 mb, according to the line "total<br />
alloc") seems to be a little too much for our simple task. We will dig<br />
deeper and find where we a wasting it.<br />
<br />
Let's examine memory consumption a little closer via so-called "heap<br />
profiles". Run <tt>./cd-fit +RTS -hb</tt>. This produces "biographical<br />
heap profile", which tells us how various parts of the memory were<br />
used during the program run time. Heap profile was saved to<br />
"cd-fit.hp". It is next to impossible to read and comprehend it as is,<br />
so use "hp2ps cd-fit.hp" to produce a nice PostScript picture which<br />
is worth a thouthand words. View it with "gv" or "ghostview" or "full<br />
Adobe Acrobat (not Reader)". (This and subsequent pictures are<br />
'''not''' attached here).<br />
<br />
Notice that most of the graph is taken up by region marked as "VOID". <br />
This means that memory allocated was never used. Notice that there is<br />
'''no''' areas marked as "USE", "LAG" or "DRAG". Seems like our<br />
program hardly uses '''any''' of the allocated memory at all. Wait a<br />
minute! How could that be? Surely it must use something when it packs<br />
to the imaginary disks of 50000 bytes those random-generated<br />
directories which are 10 to 1400 Mb in size.... Oops. Severe size<br />
mismatch. We should have spotted it earlier, when we were timing<br />
<tt>precomputeDisksFor</tt>. Scroll back and observe how each run<br />
returned the very same result - empty directory set.<br />
<br />
Our random directories are too big, but nevertheless code spends time<br />
and memory trying to "pack" them. Obviously,<br />
<tt>precomputeDisksFor</tt> (which is responsible for 90% of total<br />
memory consumption and run time) is flawed in some way.<br />
<br />
Let's take a closer look at what takes up so much memory. Run<br />
<tt>./cd-fit +RTS -h -hbvoid</tt> and produce PostScript picture for<br />
this memory profile. This will give us detailed breakdown of all<br />
memory whose "biography" shows that it's been "VOID" (unused). My<br />
picture (and I presume that yours as well) shows that VOID memory<br />
comprises of "thunks" labeled "precomputeDisksFor/pre...". We could<br />
safely assume that second word would be "precomp" (You wonder why?<br />
Look again at the code and try to find function named "pre.*" which is<br />
called from inside <tt>precomputeDisksFor</tt>)<br />
<br />
This means that memory has been taken by the list generated inside<br />
"precomp". Rumor has it that memory leaks with Haskell are caused by<br />
either too little lazyness or too much lazyness. It seems like we have<br />
too little lazyness here: we evaluate more elements of the list that<br />
we actually need and keep them from being garbage-collected. <br />
<br />
Note how we look up element from "precomp" in this piece of code:<br />
<br />
case [ DirPack (dir_size d + s) (d:ds) | d <- filter ( (inRange (1,limit)).dir_size ) dirs<br />
, dir_size d > 0<br />
, let (DirPack s ds)=precomp!!(limit - dir_size d)<br />
, d `notElem` ds<br />
<br />
<br />
Obviously, the whole list generated by "precomp" must be kept in<br />
memory for such lookups, since we can't be sure that some element<br />
could be garbage collected and will not be needed again.<br />
<br />
Let's rewrite the code to eliminate the list:<br />
<br />
-- Let the `bestDisk x' be the "most tightly packed" disk of total <br />
-- size no more than `x'.<br />
-- How to calculate `bestDisk'? Lets opt for a recursive definition:<br />
-- Recursion base: best packed disk of size 0 is empty and best-packed<br />
-- disk for empty list of directories on it is also empty.<br />
bestDisk 0 _ = DirPack 0 []<br />
bestDisk _ [] = DirPack 0 []<br />
-- Recursion step: for size `limit`, bigger than 0, best packed disk is<br />
-- comptued as follows:<br />
bestDisk limit dirs =<br />
-- Take all non-empty dirs that could possibly fit to that disk by itself.<br />
-- Consider them one by one. Let the size of particular dir be `dir_size d'.<br />
-- Let's add it to the best-packed disk of size <= (limit - dir_size d), thus<br />
-- producing the disk of size <= limit. Lets do that for all "candidate" dirs that<br />
-- are not yet on our disk:<br />
case [ DirPack (dir_size d + s) (d:ds) | d <- filter ( (inRange (1,limit)).dir_size ) dirs<br />
, dir_size d > 0<br />
, let (DirPack s ds)= bestDisk (limit - dir_size d) dirs <br />
, d `notElem` ds<br />
] of<br />
-- We either fail to add any dirs (probably, because all of them too big).<br />
-- Well, just report that disk must be left empty:<br />
[] -> DirPack 0 []<br />
-- Or we produce some alternative packings. Let's choose the best of them all:<br />
packs -> maximumBy cmpSize packs<br />
<br />
cmpSize a b = compare (pack_size a) (pack_size b)<br />
<br />
dynamic_pack limit dirs = bestDisk limit dirs<br />
<br />
<br />
Compile the profiling version of this code and obtain the overall<br />
execution profile (with "+RTS -p"). You'll get something like this:<br />
<br />
cd-fit +RTS -p -RTS<br />
<br />
total time = 0.00 secs (0 ticks @ 20 ms)<br />
total alloc = 1,129,520 bytes (excludes profiling overheads)<br />
<br />
COST CENTRE MODULE %time %alloc<br />
<br />
CAF GHC.Float 0.0 4.4<br />
main Main 0.0 93.9<br />
<br />
individual inherited<br />
COST CENTRE MODULE no. entries %time %alloc %time %alloc<br />
MAIN MAIN 1 0 0.0 0.0 0.0 100.0<br />
main Main 180 1 0.0 93.9 0.0 94.2<br />
prop_dynamic_pack_small_disk Main 181 100 0.0 0.0 0.0 0.3<br />
dynamic_pack Main 182 200 0.0 0.2 0.0 0.3<br />
bestDisk Main 183 200 0.0 0.1 0.0 0.1<br />
<br />
We achieved the major improvement: memory consumption is reduced by factor<br />
of 700! Now we could test the code on the "real task" - change the<br />
code to run the test for packing the full-sized disk:<br />
<br />
main = quickCheck prop_dynamic_pack_is_fixpoint<br />
<br />
Compile with profiling and run (with "+RTS -p"). If you are not lucky<br />
and a considerably big test set would be randomly generated for your<br />
runs, you'll have to wait. And wait even more. And more.<br />
<br />
Go make some tea. Drink it. Read some Tolstoi (Do you have "War and<br />
peace" handy?). Chances are that by the time you are done with<br />
Tolstoi, program will still be running (just take my word on it, don't<br />
check).<br />
<br />
If you are lucky, your program will finish fast enough and leave you<br />
with profile. According to a profile, program spends 99% of its time<br />
inside <tt>bestDisk</tt>. Could we speed up <tt>bestDisk</tt> somehow?<br />
<br />
Note that <tt>bestDisk</tt> performs several simple calculation for<br />
which it must call itself. However, it is done rather inefficiently -<br />
each time we pass to <tt>bestDisk</tt> the exact same set of<br />
directories as it was called with, even if we have already "packed"<br />
some of them. Let's amend this:<br />
<br />
case [ DirPack (dir_size d + s) (d:ds) | let small_enough = filter ( (inRange (1,limit)).dir_size ) dirs<br />
, d <- small_enough<br />
, dir_size d > 0<br />
, let (DirPack s ds)= bestDisk (limit - dir_size d) (delete d small_enough)<br />
] of<br />
<br />
Recompile and run again. Runtimes could be lengthy, but bearable, and<br />
number of times <tt>bestDisk</tt> is called (according to the profile)<br />
should decrease significantly. <br />
<br />
Finally, let's compare both packing algorithms. Intuitively, we feel<br />
that greedy algorithm should produce worse results, don't we? Lets put<br />
this feeling to the test:<br />
<br />
prop_greedy_pack_is_no_better_than_dynamic_pack ds =<br />
pack_size (greedy_pack ds) <= pack_size (dynamic_pack media_size ds)<br />
<br />
Verify that it is indeed so by running <tt>quickCheck</tt> for this<br />
test several time. I feel that this concludes our knapsacking<br />
exercises. <br />
<br />
Adventurous readers could continue further by implementing so-called<br />
"scaling" for <tt>dynamic_pack</tt> where we divide all directory<br />
sizes and medium size by the size of the smallest directory to proceed<br />
with smaller numbers (which promises faster runtimes). <br />
<br />
== Chapter 5: Where do you want to go tomorrow? ==<br />
<br />
As the name implies, the author is open for proposals - where should<br />
we go next? I had networking + xml/xmpp in mind, but it might be too<br />
heavy and too narrow for most of the readers.<br />
<br />
What do you think? Drop me a line.<br />
<br />
== Chapter 400: Monads up close ==<br />
<br />
Google "All about [[monad]]s" and read it. 'Nuff said :)<br />
<br />
== Chapter 500: IO up close ==<br />
<br />
Shows that:<br />
<br />
c = do a <- someAction<br />
b <- someOtherAction<br />
print (bar b)<br />
print (foo a)<br />
print "done"<br />
<br />
really is just a syntax sugar for:<br />
<br />
c = someAction >>= \a -><br />
someOtherAction >>= \b -><br />
print (bar b) >><br />
print (foo a) >><br />
print "done"<br />
<br />
and explains about ">>=" and ">>". Oh wait. This was already explained<br />
in Chapter 400 :)<br />
<br />
== Chapter 9999: Installing Haskell Compiler/Interpreter and all necessary software ==<br />
<br />
Plenty of material on this on the web and this wiki. Just go get<br />
yourself installation of [[GHC]] (6.4 or above) or [[Hugs]] (v200311 or<br />
above) and "[[darcs]]", which we will use for version control.<br />
<br />
== Chapter 10000: Thanks! ==<br />
<br />
Thanks for comments, proofreading, good advice and kind words go to: Helge,<br />
alt, dottedmag, Paul Moore, Ben Rudiak-Gould, Jim Wilkinson, avalez, Martin<br />
Percossi, SpellingNazi, Davor Cubranic, Brett Giles, Stdrange, Brian Chrisman.<br />
If I should have mentioned YOU and forgot - tell me so.<br />
<br />
Without you I would have stopped after Chapter 1 :)</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Infix_expressions&diff=2967Infix expressions2006-03-16T20:50:10Z<p>HenkJanVanTuyl: </p>
<hr />
<div><pre><br />
WORK IN PROGRESS<br />
<br />
From: dons@cse.unsw.edu.au (Donald Bruce Stewart)<br />
To: Simon Peyton-Jones <simonpj@microsoft.com><br />
Date: Wed, 15 Mar 2006 23:25:34 +1100<br />
Cc: haskell-prime@haskell.org, oleg@pobox.com<br />
Subject: Re: Infix expressions<br />
<br />
simonpj:<br />
> I often wish that cool tricks like this could be collected on the<br />
> Haskell web site. Now that it's a wiki, anyone could do that.<br />
<br />
Yes, this is _exactly_ the kind of thing to add to the Idioms <br />
page of the wiki, here: <br />
http://www.haskell.org/haskellwiki/Category:Idioms <br />
<br />
So if anyone knows of an interesting Haskell trick, and wants to write<br />
about it, add a page!<br />
<br />
We should take advantage of the fact we have a lot of good authors in<br />
the community to document all the interesting things that we come up<br />
with <br />
<br />
-- Don<br />
<br />
> <br />
> Simon<br />
> <br />
> | -----Original Message-----<br />
> | From: haskell-prime-bounces@haskell.org<br />
> [mailto:haskell-prime-bounces@haskell.org] On Behalf Of<br />
> | oleg@pobox.com<br />
> | Sent: 15 March 2006 04:34<br />
> | To: doaitse@cs.uu.nl; haskell-prime@haskell.org<br />
> | Subject: Infix expressions<br />
> | <br />
> | <br />
> | Doaitse Swierstra wrote:<br />
> | > In Haskell we write `f` in order to infixify the identifier f. In<br />
> ABC<br />
> | > the stuff between backquotes is not limited to an identifier, but<br />
> any<br />
> | > expression may occur there. This would allow one to write e.g.<br />
> | ><br />
> | > xs `zipWith (+)` ys<br />
> | <br />
> | Chung-chieh Shan and Dylan Thurston showed the Haskell98 solution for<br />
> | exactly the same example, in their article `Infix expressions',<br />
> | back in 2002:<br />
> |<br />
> http://www.haskell.org/pipermail/haskell-cafe/2002-July/003215.html<br />
> | <br />
> | For ease of reference, here's their elegant solution:<br />
> | <br />
> | > infixr 0 -:, :-<br />
> | > data Infix f y = f :- y<br />
> | > x -:f:- y = x `f` y<br />
> | ><br />
> | > main = print $ [1,2,3] -: zipWith (+) :- [4,5,6]<br />
> | <br />
> | <br />
> | For completeness, here's the `dual':<br />
> | <br />
> | > infixr 5 -!<br />
> | > (-!) = flip ($)<br />
> | > infixr 5 !-<br />
> | > (!-) = ($)<br />
> | ><br />
> | > add2 x y = x + y<br />
> | > add3 x y z = x + y + z<br />
> | > add4 x y z u = x + y + z + u<br />
> | ><br />
> | > testa1 = 1 -! add2 !- 3 + 4<br />
> | > testa2 = 1 -! add3 1 !- 3 + 4<br />
> | > testa3 = 1 - 2 -! add4 1 5 !- 3 * 4<br />
> | <br />
> | All code is Haskell98.<br />
> | _______________________________________________<br />
:<br />
:<br />
_______________________________________________<br />
Haskell-prime mailing list<br />
Haskell-prime@haskell.org<br />
http://haskell.org/mailman/listinfo/haskell-prime<br />
<br />
</pre><br />
----<br />
<br />
[[Category:Idioms]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=InfixExpressions&diff=2966InfixExpressions2006-03-16T20:43:18Z<p>HenkJanVanTuyl: InfixExpressions moved to Infix Expressions: Get rid of CamelCase</p>
<hr />
<div>#redirect [[Infix Expressions]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Infix_expressions&diff=2965Infix expressions2006-03-16T20:43:18Z<p>HenkJanVanTuyl: InfixExpressions moved to Infix Expressions</p>
<hr />
<div><pre><br />
WORK IN PROGRESS<br />
<br />
Display all headersFrom: dons@cse.unsw.edu.au (Donald Bruce Stewart)<br />
To: Simon Peyton-Jones <simonpj@microsoft.com><br />
Date: Wed, 15 Mar 2006 23:25:34 +1100<br />
Cc: haskell-prime@haskell.org, oleg@pobox.com<br />
Subject: Re: Infix expressions<br />
<br />
simonpj:<br />
> I often wish that cool tricks like this could be collected on the<br />
> Haskell web site. Now that it's a wiki, anyone could do that.<br />
<br />
Yes, this is _exactly_ the kind of thing to add to the Idioms <br />
page of the wiki, here: <br />
http://www.haskell.org/haskellwiki/Category:Idioms <br />
<br />
So if anyone knows of an interesting Haskell trick, and wants to write<br />
about it, add a page!<br />
<br />
We should take advantage of the fact we have a lot of good authors in<br />
the community to document all the interesting things that we come up<br />
with <br />
<br />
-- Don<br />
<br />
> <br />
> Simon<br />
> <br />
> | -----Original Message-----<br />
> | From: haskell-prime-bounces@haskell.org<br />
> [mailto:haskell-prime-bounces@haskell.org] On Behalf Of<br />
> | oleg@pobox.com<br />
> | Sent: 15 March 2006 04:34<br />
> | To: doaitse@cs.uu.nl; haskell-prime@haskell.org<br />
> | Subject: Infix expressions<br />
> | <br />
> | <br />
> | Doaitse Swierstra wrote:<br />
> | > In Haskell we write `f` in order to infixify the identifier f. In<br />
> ABC<br />
> | > the stuff between backquotes is not limited to an identifier, but<br />
> any<br />
> | > expression may occur there. This would allow one to write e.g.<br />
> | ><br />
> | > xs `zipWith (+)` ys<br />
> | <br />
> | Chung-chieh Shan and Dylan Thurston showed the Haskell98 solution for<br />
> | exactly the same example, in their article `Infix expressions',<br />
> | back in 2002:<br />
> |<br />
> http://www.haskell.org/pipermail/haskell-cafe/2002-July/003215.html<br />
> | <br />
> | For ease of reference, here's their elegant solution:<br />
> | <br />
> | > infixr 0 -:, :-<br />
> | > data Infix f y = f :- y<br />
> | > x -:f:- y = x `f` y<br />
> | ><br />
> | > main = print $ [1,2,3] -: zipWith (+) :- [4,5,6]<br />
> | <br />
> | <br />
> | For completeness, here's the `dual':<br />
> | <br />
> | > infixr 5 -!<br />
> | > (-!) = flip ($)<br />
> | > infixr 5 !-<br />
> | > (!-) = ($)<br />
> | ><br />
> | > add2 x y = x + y<br />
> | > add3 x y z = x + y + z<br />
> | > add4 x y z u = x + y + z + u<br />
> | ><br />
> | > testa1 = 1 -! add2 !- 3 + 4<br />
> | > testa2 = 1 -! add3 1 !- 3 + 4<br />
> | > testa3 = 1 - 2 -! add4 1 5 !- 3 * 4<br />
> | <br />
> | All code is Haskell98.<br />
> | _______________________________________________<br />
> | Haskell-prime mailing list<br />
> | Haskell-prime@haskell.org<br />
> | http://haskell.org/mailman/listinfo/haskell-prime<br />
> _______________________________________________<br />
> Haskell-prime mailing list<br />
> Haskell-prime@haskell.org<br />
> http://haskell.org/mailman/listinfo/haskell-prime<br />
_______________________________________________<br />
Haskell-prime mailing list<br />
Haskell-prime@haskell.org<br />
http://haskell.org/mailman/listinfo/haskell-prime<br />
<br />
</pre><br />
----<br />
<br />
[[Category:Idioms]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Infix_expressions&diff=2964Infix expressions2006-03-16T20:42:10Z<p>HenkJanVanTuyl: </p>
<hr />
<div><pre><br />
WORK IN PROGRESS<br />
<br />
Display all headersFrom: dons@cse.unsw.edu.au (Donald Bruce Stewart)<br />
To: Simon Peyton-Jones <simonpj@microsoft.com><br />
Date: Wed, 15 Mar 2006 23:25:34 +1100<br />
Cc: haskell-prime@haskell.org, oleg@pobox.com<br />
Subject: Re: Infix expressions<br />
<br />
simonpj:<br />
> I often wish that cool tricks like this could be collected on the<br />
> Haskell web site. Now that it's a wiki, anyone could do that.<br />
<br />
Yes, this is _exactly_ the kind of thing to add to the Idioms <br />
page of the wiki, here: <br />
http://www.haskell.org/haskellwiki/Category:Idioms <br />
<br />
So if anyone knows of an interesting Haskell trick, and wants to write<br />
about it, add a page!<br />
<br />
We should take advantage of the fact we have a lot of good authors in<br />
the community to document all the interesting things that we come up<br />
with <br />
<br />
-- Don<br />
<br />
> <br />
> Simon<br />
> <br />
> | -----Original Message-----<br />
> | From: haskell-prime-bounces@haskell.org<br />
> [mailto:haskell-prime-bounces@haskell.org] On Behalf Of<br />
> | oleg@pobox.com<br />
> | Sent: 15 March 2006 04:34<br />
> | To: doaitse@cs.uu.nl; haskell-prime@haskell.org<br />
> | Subject: Infix expressions<br />
> | <br />
> | <br />
> | Doaitse Swierstra wrote:<br />
> | > In Haskell we write `f` in order to infixify the identifier f. In<br />
> ABC<br />
> | > the stuff between backquotes is not limited to an identifier, but<br />
> any<br />
> | > expression may occur there. This would allow one to write e.g.<br />
> | ><br />
> | > xs `zipWith (+)` ys<br />
> | <br />
> | Chung-chieh Shan and Dylan Thurston showed the Haskell98 solution for<br />
> | exactly the same example, in their article `Infix expressions',<br />
> | back in 2002:<br />
> |<br />
> http://www.haskell.org/pipermail/haskell-cafe/2002-July/003215.html<br />
> | <br />
> | For ease of reference, here's their elegant solution:<br />
> | <br />
> | > infixr 0 -:, :-<br />
> | > data Infix f y = f :- y<br />
> | > x -:f:- y = x `f` y<br />
> | ><br />
> | > main = print $ [1,2,3] -: zipWith (+) :- [4,5,6]<br />
> | <br />
> | <br />
> | For completeness, here's the `dual':<br />
> | <br />
> | > infixr 5 -!<br />
> | > (-!) = flip ($)<br />
> | > infixr 5 !-<br />
> | > (!-) = ($)<br />
> | ><br />
> | > add2 x y = x + y<br />
> | > add3 x y z = x + y + z<br />
> | > add4 x y z u = x + y + z + u<br />
> | ><br />
> | > testa1 = 1 -! add2 !- 3 + 4<br />
> | > testa2 = 1 -! add3 1 !- 3 + 4<br />
> | > testa3 = 1 - 2 -! add4 1 5 !- 3 * 4<br />
> | <br />
> | All code is Haskell98.<br />
> | _______________________________________________<br />
> | Haskell-prime mailing list<br />
> | Haskell-prime@haskell.org<br />
> | http://haskell.org/mailman/listinfo/haskell-prime<br />
> _______________________________________________<br />
> Haskell-prime mailing list<br />
> Haskell-prime@haskell.org<br />
> http://haskell.org/mailman/listinfo/haskell-prime<br />
_______________________________________________<br />
Haskell-prime mailing list<br />
Haskell-prime@haskell.org<br />
http://haskell.org/mailman/listinfo/haskell-prime<br />
<br />
</pre><br />
----<br />
<br />
[[Category:Idioms]]</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/Streams&diff=2264Library/Streams2006-02-11T16:01:54Z<p>HenkJanVanTuyl: </p>
<hr />
<div>== Introduction ==<br />
<br />
=== Streams: the extensible I/O library ===<br />
<br />
I have developed a new I/O library that IMHO is so sharp that it can<br />
eventually replace the current I/O facilities based on using Handles.<br />
The main advantage of the new library is its strong modular design using<br />
typeclasses. The library consists of small independent modules, each<br />
implementing one type of stream (file, memory buffer, pipe) or one<br />
part of common stream functionality (buffering, Char encoding,<br />
locking). 3rd-party libs can easily add new stream types and new common<br />
functionality. Other benefits of the new library include support for<br />
streams functioning in any monad, Hugs and GHC compatibility, high<br />
speed and an easy migration path from the existing I/O library.<br />
<br />
The Streams library is heavily based on the HVIO module written by John<br />
Goerzen. I especially want to thank John for his clever design and<br />
implementation. Really, I just renamed HVIO to Stream and presented<br />
this as my own work. :) Further development direction was inspired<br />
by the "New I/O library" written by Simon Marlow.<br />
<br />
=== Simple Streams ===<br />
<br />
The key concept of the lib is the Stream class, whose interface mimics<br />
familiar interface for Handles, just with "h" replaced with "v" in<br />
function names:<br />
<br />
class (Monad m) => Stream m h where<br />
vPutStrLn :: h -> String -> m ()<br />
vGetContents :: h -> m String<br />
vIsEOF :: h -> m Bool<br />
vClose :: h -> m ()<br />
....................<br />
<br />
This means that you already know how to use any stream! The Stream interface<br />
currently has 8 implementations: a Handle itself, raw files, pipes,<br />
memory buffers and string buffers. Future plans include support for<br />
memory-mapped files, sockets, circular memory buffers for interprocess<br />
communication and UArray-based streams.<br />
<br />
By themselves, these Stream implementations are rather simple. Basically,<br />
to implement new Stream type, it's enough to provide vPutBuf/vGetBuf<br />
operations, or even vGetChar/vPutChar. The latter way, although <br />
inefficient, allows us to implement streams that can work in any monad.<br />
StringReader and StringBuffer streams use this to provide string-based<br />
Stream class implementations both for IO and ST monads. Yes, you can<br />
use the full power of Stream operations inside the ST monad!<br />
<br />
=== Layers of Functionality ===<br />
<br />
All additional functionality is implemented via Stream Transformers,<br />
which are just parameterized Streams, whose parameters also<br />
implement the Stream interface. This allows you to apply any number of stream<br />
transformers to the raw stream and then use the result as an ordinary<br />
Stream. For example:<br />
<br />
h <- openRawFD "test" WriteMode<br />
>>= bufferBlockStream<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
<br />
This code creates a new FD, which represents a raw file, and then adds<br />
to this Stream buffering, Char encoding and locking functionality. The<br />
result type of "h" is something like this:<br />
<br />
WithLocking (WithEncoding (BufferedBlockStream FD))<br />
<br />
The complete type, as well as all the intermediate types, implements the Stream<br />
interface. Each transformer intercepts operations corresponding to its<br />
nature, and passes the rest through. For example, the encoding transformer<br />
intercepts only vGetChar/vPutChar operations and translates them to<br />
the sequences of vGetByte/vPutByte calls of the lower-level stream.<br />
The locking transformer just wraps any operation in the locking wrapper.<br />
<br />
We can trace, for example, the execution of a "vPutBuf" operation on the<br />
above-constructed Stream. First, the locking transformer acquires a lock<br />
and then passes this call to the next level. Then the encoding transformer does<br />
nothing and passes this call to the next level. The buffering<br />
transformer flushes the current buffer and passes the call further.<br />
Finally, FD itself performs the operation after all these<br />
preparations and on the returning path, the locking transformer release<br />
its lock.<br />
<br />
As another example, the "vPutChar" call on this Stream is<br />
transformed (after locking) into several "vPutByte" calls by the<br />
encoding transformer, and these bytes go to the buffer in the<br />
buffering transformer, with or without a subsequent call to the FD's<br />
"vPutBuf".<br />
<br />
=== Modularity ===<br />
<br />
As you can see, stream transformers really are independent of each<br />
other. This allows you to use them on any stream and in any combination<br />
(but you should apply them in proper order - buffering, then Char<br />
encoding, then locking). As a result, you can apply to the stream<br />
only the transformers that you really need. If you don't use the<br />
stream in multiple threads, you don't need to apply the locking<br />
transformer. If you don't use any encodings other than Latin-1 -- or<br />
don't use text I/O at all -- you don't need an encoding transformer.<br />
Moreover, you may not even need to know anything about the UserData transformer<br />
until you actually need to use it :)<br />
<br />
Both streams and stream transformers can be implemented by 3rd-party<br />
libraries. Streams and transformers from arbitrary libraries will<br />
seamlessly work together as long as they properly implement the Stream<br />
interface. My future plans include implementation of an on-the-fly<br />
(de)compression transformer and I will be happy to see 3rd-party<br />
transformers that intercept vGetBuf/vPutBuf calls and use select(),<br />
kqueue() and other methods to overlap I/O operations.<br />
<br />
=== Speed ===<br />
<br />
A quick comment about speed: it's fast enough -- 10-50 MB/s (depending<br />
on the type of operation) on a 1GHz cpu. The Handle operations, for comparison, <br />
show speed of 1-10 mb/s on the same computer. But that don't means that each <br />
and any operation in new library is 10 times faster. Strict I/O (including <br />
vGetChar/vPutChar) is a LOT faster. I included a demonstration of this <br />
fascinating speed as "Examples/wc.hs". If you need a really high speed, <br />
don't forget to increase buffer size with "vSetBuffering".<br />
<br />
On the other side, lazy I/O (including any operations that receive or return <br />
strings) show only modest speedup. This is limited by Haskell/GHC itself and <br />
I can't do much to get around these limits. Instead, I plan to provide support <br />
for I/O using packed strings. This will allow to write I/O-intensive Haskell <br />
programs that are as fast as their C counterparts.<br />
<br />
Other sources of slowness includes using of locking transformer (if you need <br />
to do this, try use "lock" around speed-critical algorithms) and complex class <br />
structure, what may be avoided by using "forall" types (I'm not sure, Simon <br />
Marlow can enlighten this topic).<br />
<br />
The library includes benchmarking code in the file "Examples/StreamsBenchmark.hs"<br />
<br />
=== Stage of Development ===<br />
<br />
The library is currently at the beta stage. It contains a number of<br />
known minor problems and an unknown number of yet-to-be-discovered bugs.<br />
It is not properly documented, doesn't include QuickCheck tests, is not<br />
cabalized, and not all "h*" operations have their "v*" equivalents yet.<br />
If anyone wants to join this effort in order to help fix these oddities<br />
and prepare the lib for inclusion in the standard libraries suite, I would<br />
be really happy. :) I will also be happy (although much less ;) to see<br />
bug reports and suggestions about its interface and internal<br />
organization. It's just a first public version, so we still can change<br />
everything here!<br />
<br />
In particular, this wiki page is an official library documentation. <br />
Please continue to improve it and add more information about using library.<br />
<br />
=== Hugs support ===<br />
<br />
The library fully supports Hugs 2003 and Hugs 2005, but<br />
<br />
1) support for FD and MMFile is temporarily disabled because I don't know how <br />
to build DLLs<br />
<br />
2) Hugs 2003 doesn't include support for "instance Bits Word" and vGetBuf/vPutBuf, <br />
so you need to add these implementations manually or delete the lines that use it <br />
(look for "2003" in the sources)<br />
<br />
3) WinHugs doesn't support preprocessing, so I included the MakeHugs.cmd script <br />
to preprocess source files using cpphs<br />
<br />
=== Support for other compilers ===<br />
<br />
Main disadvantage of the library is that it supports only Hugs and GHC<br />
because of using extensions in type classe system. I think that it<br />
can be made H98-compatible at the cost of excluding support for non-IO<br />
monads. I will try to make such a stripped version for other compilers<br />
if people are interested.<br />
<br />
=== Changes in last versions ===<br />
<br />
User-visible improvements made in Streams library since version 0.1 (6 Feb 2006):<br />
<br />
0.1a (6 Feb 2006)<br />
- Fixed bug: System.MMFile was uncompilable on non-Windows systems<br />
<br />
0.1b (9 Feb 2006)<br />
- Fixed bug: very slow WithLocking.vGetLine<br />
- Fixed bug: System.FD was also uncompilable on non-Windows systems<br />
<br />
<br />
<br />
== Overview of Stream Transformers ==<br />
<br />
=== Buffering ===<br />
<br />
There are three buffering transformers. Each buffering transformer<br />
implements support for vGetByte, vPutChar, vGetContents and other<br />
byte- and text-oriented operations for the streams, which by themselves<br />
support only vGetBuf/vPutBuf (or vReceiveBuf/vSendBuf) operations.<br />
<br />
The first transformer can be applied to any stream supporting<br />
vGetBuf/vPutBuf. This is applied by the operation "bufferBlockStream". The<br />
well-known vSetBuffering/vGetBuffering operations are intercepted by<br />
this transformer and used to control buffer size. At this moment, only<br />
BlockBuffering is implemented, while LineBuffering and NoBuffering are<br />
only in the planning stages.<br />
<br />
Two other transformers can be applied to streams that implement<br />
vReceiveBuf/vSendBuf operations -- that is, streams whose data<br />
reside in memory, including in-memory streams and memory-mapped<br />
files. In these cases, the buffering transformer doesn't need to allocate<br />
a buffer itself, it just requests from the underlying stream the address and<br />
size of the next available portion of data. Nevertheless, the final<br />
result is the same -- we get support for all byte- and text-oriented<br />
I/O operations. The "bufferMemoryStream" operation can be applied to any<br />
memory-based stream to add buffering to it. The "bufferMemoryStreamUnchecked" <br />
operation (which implements the third buffering<br />
transformer) can be used instead, if you can guarantee that I/O<br />
operations can't overflow the used buffer.<br />
<br />
=== Encoding ===<br />
<br />
The Char encoding transformer allows you to encode each Char written to the<br />
stream as a sequence of bytes, implementing UTF and other encodings.<br />
This transformer can be applied to any stream implementing<br />
vGetByte/vPutByte operations and in return it implements<br />
vGetChar/vPutChar and all other text-oriented operations. This<br />
transformer can be applied to a stream with the "withEncoding encoding"<br />
operation, where `encoding` may be `latin1`, `utf8` or any other<br />
encoding that you (or a 3rd-party lib) implement. Look at the<br />
"Data.CharEncoding" module to see how to implement new encodings.<br />
Encoding of streams created with the "withEncoding" operation can be<br />
changed at any moment with "vSetEncoding" and queried with<br />
"vGetEncoding". See examples of their usage in the file<br />
"Examples/CharEncoding.hs"<br />
<br />
=== Locking ===<br />
<br />
The locking transformer ensures that the stream is properly shared by <br />
several threads. You already know enough about its basic usage --<br />
"withLocking" applies this transformer to the stream and all the<br />
required locking is performed automagically. You can also use "lock"<br />
operations to acquire the lock explicitly during multiple operations:<br />
<br />
lock h $ \h -> do<br />
savedpos <- vTell h<br />
vSeek h AbsoluteSeek 100<br />
vPutStr h ":-)"<br />
vSeek h AbsoluteSeek savedpos<br />
<br />
See the file "Examples/Locking.hs" for examples of using locking transformer.<br />
<br />
=== Attaching user data ===<br />
<br />
This transformer allows you to attach arbitrary data to any Stream. It does <br />
nothing extraordinary except that the stream with attached data is the proper <br />
Stream, again. See example of its usage in the file "Examples/UserData.hs" <br />
<br />
== Overview of Stream Types ==<br />
<br />
=== Handle (legacy way to access files/sockets) ===<br />
<br />
"Handle" is an instance of the Stream class, with a straightforward implementation. <br />
You can use the<br />
Char encoding transformer with Handles. Although Handles implement<br />
buffering and locking by themselves, you may also be interested in<br />
applying these transformers to the Handle type. This has<br />
benefits -- "bufferBlockStream" works faster than internal Handle<br />
buffering, and the locking transformer enables the use of a "lock" operation to<br />
create a lock around a sequence of operations. Moreover, the locking<br />
transformer should be used to ensure proper multi-threading operation<br />
of Handle with added encoding or buffering facilities.<br />
<br />
=== FD (new way to access files) ===<br />
<br />
The new method of using files, independent of the existing I/O<br />
library, is implemented with the FD type. FD is just an Int representing a<br />
POSIX file descriptor and the FD type implements only basic Stream I/O<br />
operations - vGetBuf and vPutBuf. So, to create a full-featured FD-based<br />
stream, you need to apply buffering transformers. Therefore, the library<br />
defines two ways to open files with FD - openRawFD/openRawBinaryFD<br />
just creates FD, while openFD/openBinaryFD creates FD and immediatelly<br />
apply buffering transformer (bufferBlockStream) to it. In most cases<br />
you will use the latter operations. Both pairs mimic the arguments and<br />
behaviour of well-known Handle operations openFile/openBinaryFile, so<br />
you already know how to use them. Other transformers may be used then<br />
as you need. So, abovementioned example can be abbreviated to:<br />
<br />
h <- openFD "test" WriteMode<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
<br />
Thus, to switch from the existing I/O library to using Streams, you<br />
need only to replace "h" with "v" in the names of Handle operations, and<br />
replace openFile/openBinaryFile calls with openFD/openBinaryFD while<br />
adding the "withLocking" transformer to files used in multiple threads.<br />
That's all! <br />
<br />
<br />
For example, the following code:<br />
<br />
h <- openFile "test" ReadMode<br />
text <- hGetContents h<br />
hClose h<br />
<br />
should be translated to:<br />
<br />
h <- openFD "test" ReadMode<br />
-- >>= withLocking -- needed only for multi-threaded usage<br />
text <- vGetContents h<br />
vClose h<br />
<br />
<br />
File "Examples/FD.hs" will show you the FD usage.<br />
<br />
<br />
=== MemBuf (memory-resident stream) ===<br />
<br />
MemBuf is a stream type, that keeps its contents in memory buffer.<br />
There are two types of MemBufs you can create - you can either open<br />
existing memory buffer with "openMemBuf ptr size" or create new one<br />
with "createMemBuf initsize". MemBuf opened by "openMemBuf" will be<br />
never resized or moved in memory, and will not be freed by "vClose".<br />
MemBuf created by "createMemBuf" will grow as needed, can be manually<br />
resized by "vSetFileSize" operation, and is automatically freed by<br />
"vClose".<br />
<br />
Actually, raw MemBufs created by the "createRawMemBuf" and "openRawMemBuf"<br />
operations, while createMemBuf/openMemBuf incorporate an additional<br />
"bufferMemoryStream" call (as you should remember, buffering adds vGetChar,<br />
vPutStr and other text- and byte-I/O operations on top of vReceiveBuf<br />
and vSendBuf). You can also apply Char encoding and locking<br />
transformers to these streams. The "saveToFile" and "readFromFile" operations <br />
provide an easy way to save/restore buffer contents in a file. <br />
<br />
File "Examples/MemBuf.hs" demonstrates the usage of MemBuf.<br />
<br />
=== FunctionsMemoryStream ===<br />
<br />
This Stream type allows implementation of arbitrary streams, just by<br />
providing three functions that implement vReceiveBuf, vSendBuf and cleanup<br />
operations. It seems that this Stream type is of interest only for my<br />
own program and can be scrutinized only as example of creating 3rd-party<br />
Stream types. It is named "FunctionsMemoryStream", see the sources if you<br />
are interested.<br />
<br />
=== StringReader & StringBuffer (String-based streams) ===<br />
<br />
Four remaining Stream types were part of the HVIO module and I copied their<br />
description from there:<br />
<br />
In addition to Handle, there are several pre-defined stream types for<br />
your use. 'StringReader' is a particularly interesting one. At<br />
creation time, you pass it a String. Its contents are read lazily<br />
whenever a read call is made. It can be used, therefore, to implement<br />
filters (simply initialize it with the result from, say, a map over<br />
hGetContents from another Stream object), codecs, and simple I/O<br />
testing. Because it is lazy, it needs not hold the entire string in<br />
memory. You can create a 'StringReader' with a call to<br />
'newStringReader'.<br />
<br />
'StringBuffer' is a similar type, but with a different purpose. It<br />
provides a full interface like Handle (it supports read, write and<br />
seek operations). However, it maintains an in-memory buffer with the<br />
contents of the file, rather than an actual on-disk file. You can<br />
access the entire contents of this buffer at any time. This can be<br />
quite useful for testing I/O code, or for cases where existing APIs<br />
use I/O, but you prefer a String representation. Note however that<br />
this stream type is very inefficient. You can create a 'StringBuffer'<br />
with a call to 'newStringBuffer'.<br />
<br />
One significant improvement over the original HVIO library is that<br />
'StringReader' and 'StringBuffer' can work not only in IO, but also in<br />
ST monad.<br />
<br />
=== Pipes (passing data between Haskell threads) ===<br />
<br />
Finally, there are pipes. These pipes are analogous to the Unix pipes<br />
that are available from System.Posix, but don't require Unix and work<br />
only in Haskell. When you create a pipe, you actually get two Stream<br />
objects: a 'PipeReader' and a 'PipeWriter'. You must use the<br />
'PipeWriter' in one thread and the 'PipeReader' in another thread.<br />
Data that's written to the 'PipeWriter' will then be available for<br />
reading with the 'PipeReader'. The pipes are implemented completely<br />
with existing Haskell threading primitives, and require no special<br />
operating system support. Unlike Unix pipes, these pipes cannot be<br />
used across a fork(). Also unlike Unix pipes, these pipes are<br />
portable and interact well with Haskell threads. A new pipe can be<br />
created with a call to 'newHVIOPipe'.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/Streams&diff=2213Library/Streams2006-02-06T21:38:09Z<p>HenkJanVanTuyl: </p>
<hr />
<div>== Introduction ==<br />
<br />
=== Streams: the extensible I/O library ===<br />
<br />
I have developed a new I/O library that IMHO is so sharp that it can<br />
eventually replace the current I/O facilities based on using Handles.<br />
The main advantage of the new library is its strong modular design using<br />
typeclasses. The library consists of small independent modules, each<br />
implementing one type of stream (file, memory buffer, pipe) or one<br />
part of common stream functionality (buffering, Char encoding,<br />
locking). 3rd-party libs can easily add new stream types and new common<br />
functionality. Other benefits of the new library include support for<br />
streams functioning in any monad, Hugs and GHC compatibility, high<br />
speed and an easy migration path from the existing I/O library.<br />
<br />
The Streams library is heavily based on the HVIO module written by John<br />
Goerzen. I especially want to thank John for his clever design and<br />
implementation. Really, I just renamed HVIO to Stream and presented<br />
this as my own work. :) Further development direction was inspired<br />
by the "New I/O library" written by Simon Marlow.<br />
<br />
=== Simple Streams ===<br />
<br />
The key concept of the lib is the Stream class, whose interface mimics<br />
familiar interface for Handles, just with "h" replaced with "v" in<br />
function names:<br />
<br />
class (Monad m) => Stream m h where<br />
vPutStrLn :: h -> String -> m ()<br />
vGetContents :: h -> m String<br />
vIsEOF :: h -> m Bool<br />
vClose :: h -> m ()<br />
....................<br />
<br />
This means that you already know how to use any stream! The Stream interface<br />
currently has 8 implementations: a Handle itself, raw files, pipes,<br />
memory buffers and string buffers. Future plans include support for<br />
memory-mapped files, sockets, circular memory buffers for interprocess<br />
communication and UArray-based streams.<br />
<br />
By themselves, these Stream implementations are rather simple. Basically,<br />
to implement new Stream type, it's enough to provide vPutBuf/vGetBuf<br />
operations, or even vGetChar/vPutChar. The latter way, although <br />
inefficient, allows us to implement streams that can work in any monad.<br />
StringReader and StringBuffer streams use this to provide string-based<br />
Stream class implementations both for IO and ST monads. Yes, you can<br />
use the full power of Stream operations inside the ST monad!<br />
<br />
=== Layers of Functionality ===<br />
<br />
All additional functionality is implemented via Stream Transformers,<br />
which are just parameterized Streams, whose parameters also<br />
implement the Stream interface. This allows you to apply any number of stream<br />
transformers to the raw stream and then use the result as an ordinary<br />
Stream. For example:<br />
<br />
h <- openRawFD "test" WriteMode<br />
>>= bufferBlockStream<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
<br />
This code creates a new FD, which represents a raw file, and then adds<br />
to this Stream buffering, Char encoding and locking functionality. The<br />
result type of "h" is something like this:<br />
<br />
WithLocking (WithEncoding (BufferedBlockStream FD))<br />
<br />
The complete type, as well as all the intermediate types, implements the Stream<br />
interface. Each transformer intercepts operations corresponding to its<br />
nature, and passes the rest through. For example, the encoding transformer<br />
intercepts only vGetChar/vPutChar operations and translates them to<br />
the sequences of vGetByte/vPutByte calls of the lower-level stream.<br />
The locking transformer just wraps any operation in the locking wrapper.<br />
<br />
We can trace, for example, the execution of a "vPutBuf" operation on the<br />
above-constructed Stream. First, the locking transformer acquires a lock<br />
and then passes this call to the next level. Then the encoding transformer does<br />
nothing and passes this call to the next level. The buffering<br />
transformer flushes the current buffer and passes the call further.<br />
Finally, FD itself performs the operation after all these<br />
preparations and on the returning path, the locking transformer release<br />
its lock.<br />
<br />
As another example, the "vPutChar" call on this Stream is<br />
transformed (after locking) into several "vPutByte" calls by the<br />
encoding transformer, and these bytes go to the buffer in the<br />
buffering transformer, with or without a subsequent call to the FD's<br />
"vPutBuf".<br />
<br />
=== Modularity ===<br />
<br />
As you can see, stream transformers really are independent of each<br />
other. This allows you to use them on any stream and in any combination<br />
(but you should apply them in proper order - buffering, then Char<br />
encoding, then locking). As a result, you can apply to the stream<br />
only the transformers that you really need. If you don't use the<br />
stream in multiple threads, you don't need to apply the locking<br />
transformer. If you don't use any encodings other than Latin-1 -- or<br />
don't use text I/O at all -- you don't need an encoding transformer.<br />
Moreover, you may not even need to know anything about the UserData transformer<br />
until you actually need to use it :)<br />
<br />
Both streams and stream transformers can be implemented by 3rd-party<br />
libraries. Streams and transformers from arbitrary libraries will<br />
seamlessly work together as long as they properly implement the Stream<br />
interface. My future plans include implementation of an on-the-fly<br />
(de)compression transformer and I will be happy to see 3rd-party<br />
transformers that intercept vGetBuf/vPutBuf calls and use select(),<br />
kqueue() and other methods to overlap I/O operations.<br />
<br />
=== Speed ===<br />
<br />
A quick comment about speed: it's fast enough -- 10-50 MB/s (depending<br />
on the type of operation) on a 1GHz cpu. The Handle operations, for comparison, show speed of 1-10 mb/s on the same computer. But that don't means that each and any operation in new library is 10 times faster. Strict I/O (including vGetChar/vPutChar) is a LOT faster. I included a demonstration of this fascinating speed as "Examples/wc.hs". If you need a really high speed, don't forget to increase buffer size with "vSetBuffering".<br />
<br />
On the other side, lazy I/O (including any operations that receive or return strings) show only modest speedup. This is limited by Haskell/GHC itself and I can't do much to get around these limits. Instead, I plan to provide support for I/O using packed strings. This will allow to write I/O-intensive Haskell programs that are as fast as their C counterparts.<br />
<br />
The library includes benchmarking code in the file "Examples/StreamsBenchmark.hs"<br />
<br />
=== Stage of Development ===<br />
<br />
The library is currently at the beta stage. It contains a number of<br />
known minor problems and an unknown number of yet-to-be-discovered bugs.<br />
It is not properly documented, doesn't include QuickCheck tests, is not<br />
cabalized, and not all "h*" operations have their "v*" equivalents yet.<br />
If anyone wants to join this effort in order to help fix these oddities<br />
and prepare the lib for inclusion in the standard libraries suite, I would<br />
be really happy. :) I will also be happy (although much less ;) to see<br />
bug reports and suggestions about its interface and internal<br />
organization. It's just a first public version, so we still can change<br />
everything here!<br />
<br />
=== Hugs support ===<br />
<br />
The library fully supports Hugs 2003 and Hugs 2005, but<br />
<br />
1) support for FD and MMFile is temporarily disabled because I don't know how to build DLLs<br />
<br />
2) Hugs 2003 doesn't include support for "instance Bits Word" and vGetBuf/vPutBuf, so you need<br />
to add these implementations manually or delete the lines that use it (look for "2003" in the sources)<br />
<br />
3) WinHugs doesn't support preprocessing, so I included the MakeHugs.cmd script to preprocess source<br />
files using cpphs<br />
<br />
=== Support for other compilers ===<br />
<br />
Main disadvantage of the library is that it supports only Hugs and GHC<br />
because of using extensions in type classe system. I think that it<br />
can be made H98-compatible at the cost of excluding support for non-IO<br />
monads. I will try to make such a stripped version for other compilers<br />
if people are interested.<br />
<br />
== Overview of Stream Transformers ==<br />
<br />
=== Buffering ===<br />
<br />
There are three buffering transformers. Each buffering transformer<br />
implements support for vGetByte, vPutChar, vGetContents and other<br />
byte- and text-oriented operations for the streams, which by themselves<br />
support only vGetBuf/vPutBuf (or vReceiveBuf/vSendBuf) operations.<br />
<br />
The first transformer can be applied to any stream supporting<br />
vGetBuf/vPutBuf. This is applied by the operation "bufferBlockStream". The<br />
well-known vSetBuffering/vGetBuffering operations are intercepted by<br />
this transformer and used to control buffer size. At this moment, only<br />
BlockBuffering is implemented, while LineBuffering and NoBuffering are<br />
only in the planning stages.<br />
<br />
Two other transformers can be applied to streams that implement<br />
vReceiveBuf/vSendBuf operations -- that is, streams whose data<br />
reside in memory, including in-memory streams and memory-mapped<br />
files. In these cases, the buffering transformer doesn't need to allocate<br />
a buffer itself, it just requests from the underlying stream the address and<br />
size of the next available portion of data. Nevertheless, the final<br />
result is the same -- we get support for all byte- and text-oriented<br />
I/O operations. The "bufferMemoryStream" operation can be applied to any<br />
memory-based stream to add buffering to it. The "bufferMemoryStreamUnchecked" operation (which implements the third buffering<br />
transformer) can be used instead, if you can guarantee that I/O<br />
operations can't overflow the used buffer.<br />
<br />
=== Encoding ===<br />
<br />
The Char encoding transformer allows you to encode each Char written to the<br />
stream as a sequence of bytes, implementing UTF and other encodings.<br />
This transformer can be applied to any stream implementing<br />
vGetByte/vPutByte operations and in return it implements<br />
vGetChar/vPutChar and all other text-oriented operations. This<br />
transformer can be applied to a stream with the "withEncoding encoding"<br />
operation, where `encoding` may be `latin1`, `utf8` or any other<br />
encoding that you (or a 3rd-party lib) implement. Look at the<br />
"Data.CharEncoding" module to see how to implement new encodings.<br />
Encoding of streams created with the "withEncoding" operation can be<br />
changed at any moment with "vSetEncoding" and queried with<br />
"vGetEncoding". See examples of their usage in the file<br />
"Examples/CharEncoding.hs"<br />
<br />
=== Locking ===<br />
<br />
The locking transformer ensures that the stream is properly shared by <br />
several threads. You already know enough about its basic usage --<br />
"withLocking" applies this transformer to the stream and all the<br />
required locking is performed automagically. You can also use "lock"<br />
operations to acquire the lock explicitly during multiple operations:<br />
<br />
lock h $ \h -> do<br />
savedpos <- vTell h<br />
vSeek h AbsoluteSeek 100<br />
vPutStr h ":-)"<br />
vSeek h AbsoluteSeek savedpos<br />
<br />
See the file "Examples/Locking.hs" for examples of using locking transformer.<br />
<br />
=== Attaching user data ===<br />
<br />
This transformer allows you to attach arbitrary data to any Stream. It does nothing extraordinary except that the stream with attached data is the proper Stream, again. See example of its usage in the file "Examples/UserData.hs" <br />
<br />
== Overview of Stream Types ==<br />
<br />
=== Handle (legacy way to access files/sockets) ===<br />
<br />
"Handle" is an instance of the Stream class, with a straightforward implementation. You can use the<br />
Char encoding transformer with Handles. Although Handles implement<br />
buffering and locking by themselves, you may also be interested in<br />
applying these transformers to the Handle type. This has<br />
benefits -- "bufferBlockStream" works faster than internal Handle<br />
buffering, and the locking transformer enables the use of a "lock" operation to<br />
create a lock around a sequence of operations. Moreover, the locking<br />
transformer should be used to ensure proper multi-threading operation<br />
of Handle with added encoding or buffering facilities.<br />
<br />
=== FD (new way to access files) ===<br />
<br />
The new method of using files, independent of the existing I/O<br />
library, is implemented with the FD type. FD is just an Int representing a<br />
POSIX file descriptor and the FD type implements only basic Stream I/O<br />
operations - vGetBuf and vPutBuf. So, to create a full-featured FD-based<br />
stream, you need to apply buffering transformers. Therefore, the library<br />
defines two ways to open files with FD - openRawFD/openRawBinaryFD<br />
just creates FD, while openFD/openBinaryFD creates FD and immediatelly<br />
apply buffering transformer (bufferBlockStream) to it. In most cases<br />
you will use the latter operations. Both pairs mimic the arguments and<br />
behaviour of well-known Handle operations openFile/openBinaryFile, so<br />
you already know how to use them. Other transformers may be used then<br />
as you need. So, abovementioned example can be abbreviated to:<br />
<br />
h <- openFD "test" WriteMode<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
<br />
Thus, to switch from the existing I/O library to using Streams, you<br />
need only to replace "h" with "v" in the names of Handle operations, and<br />
replace openFile/openBinaryFile calls with openFD/openBinaryFD while<br />
adding the "withLocking" transformer to files used in multiple threads.<br />
That's all! <br />
<br />
<br />
For example, the following code:<br />
<br />
h <- openFile "test" ReadMode<br />
text <- hGetContents h<br />
hClose h<br />
<br />
should be translated to:<br />
<br />
h <- openFD "test" ReadMode<br />
-- >>= withLocking -- needed only for multi-threaded usage<br />
text <- vGetContents h<br />
vClose h<br />
<br />
<br />
File "Examples/FD.hs" will show you the FD usage.<br />
<br />
<br />
=== MemBuf (memory-resident stream) ===<br />
<br />
MemBuf is a stream type, that keeps its contents in memory buffer.<br />
There are two types of MemBufs you can create - you can either open<br />
existing memory buffer with "openMemBuf ptr size" or create new one<br />
with "createMemBuf initsize". MemBuf opened by "openMemBuf" will be<br />
never resized or moved in memory, and will not be freed by "vClose".<br />
MemBuf created by "createMemBuf" will grow as needed, can be manually<br />
resized by "vSetFileSize" operation, and is automatically freed by<br />
"vClose".<br />
<br />
Actually, raw MemBufs created by the "createRawMemBuf" and "openRawMemBuf"<br />
operations, while createMemBuf/openMemBuf incorporate an additional<br />
"bufferMemoryStream" call (as you should remember, buffering adds vGetChar,<br />
vPutStr and other text- and byte-I/O operations on top of vReceiveBuf<br />
and vSendBuf). You can also apply Char encoding and locking<br />
transformers to these streams. The "saveToFile" and "readFromFile" operations provide an easy way to save/restore buffer contents in a file. <br />
<br />
File "Examples/MemBuf.hs" demonstrates the usage of MemBuf.<br />
<br />
=== FunctionsMemoryStream ===<br />
<br />
This Stream type allows implementation of arbitrary streams, just by<br />
providing three functions that implement vReceiveBuf, vSendBuf and cleanup<br />
operations. It seems that this Stream type is of interest only for my<br />
own program and can be scrutinized only as example of creating 3rd-party<br />
Stream types. It is named "FunctionsMemoryStream", see the sources if you<br />
are interested.<br />
<br />
=== StringReader & StringBuffer (String-based streams) ===<br />
<br />
Four remaining Stream types was a part of the HVIO module and I copied their<br />
description from there:<br />
<br />
In addition to Handle, there are several pre-defined stream types for<br />
your use. 'StringReader' is a particularly interesting one. At<br />
creation time, you pass it a String. Its contents are read lazily<br />
whenever a read call is made. It can be used, therefore, to implement<br />
filters (simply initialize it with the result from, say, a map over<br />
hGetContents from another Stream object), codecs, and simple I/O<br />
testing. Because it is lazy, it needs not hold the entire string in<br />
memory. You can create a 'StringReader' with a call to<br />
'newStringReader'.<br />
<br />
'StringBuffer' is a similar type, but with a different purpose. It<br />
provides a full interface like Handle (it supports read, write and<br />
seek operations). However, it maintains an in-memory buffer with the<br />
contents of the file, rather than an actual on-disk file. You can<br />
access the entire contents of this buffer at any time. This can be<br />
quite useful for testing I/O code, or for cases where existing APIs<br />
use I/O, but you prefer a String representation. Note however that<br />
this stream type is very inefficient. You can create a 'StringBuffer'<br />
with a call to 'newStringBuffer'.<br />
<br />
One significant improvement over the original HVIO library is that<br />
'StringReader' and 'StringBuffer' can work not only in IO, but also in<br />
ST monad.<br />
<br />
=== Pipes (passing data between Haskell threads) ===<br />
<br />
Finally, there are pipes. These pipes are analogous to the Unix pipes<br />
that are available from System.Posix, but don't require Unix and work<br />
only in Haskell. When you create a pipe, you actually get two Stream<br />
objects: a 'PipeReader' and a 'PipeWriter'. You must use the<br />
'PipeWriter' in one thread and the 'PipeReader' in another thread.<br />
Data that's written to the 'PipeWriter' will then be available for<br />
reading with the 'PipeReader'. The pipes are implemented completely<br />
with existing Haskell threading primitives, and require no special<br />
operating system support. Unlike Unix pipes, these pipes cannot be<br />
used across a fork(). Also unlike Unix pipes, these pipes are<br />
portable and interact well with Haskell threads. A new pipe can be<br />
created with a call to 'newHVIOPipe'.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/Streams&diff=2189Library/Streams2006-02-05T21:40:12Z<p>HenkJanVanTuyl: </p>
<hr />
<div>== Introduction ==<br />
<br />
=== Streams: the extensible I/O library ===<br />
<br />
I have developed a new I/O library that IMHO is so sharp that it can<br />
eventually replace the current I/O facilities based on using Handles.<br />
The main advantage of the new library is its strong modular design using<br />
typeclasses. The library consists of small independent modules, each<br />
implementing one type of stream (file, memory buffer, pipe) or one<br />
part of common stream functionality (buffering, Char encoding,<br />
locking). 3rd-party libs can easily add new stream types and new common<br />
functionality. Other benefits of the new library include support for<br />
streams functioning in any monad, Hugs and GHC compatibility, high<br />
speed and an easy migration path from the existing I/O library.<br />
<br />
The Streams library is heavily based on the HVIO module written by John<br />
Goerzen. I especially want to thank John for his clever design and<br />
implementation. Really, I just renamed HVIO to Stream and presented<br />
this as my own work. :) Further development direction was inspired<br />
by the "New I/O library" written by Simon Marlow.<br />
<br />
=== Simple Streams ===<br />
<br />
The key concept of the lib is the Stream class, whose interface mimics<br />
familiar interface for Handles, just with "h" replaced with "v" in<br />
function names:<br />
<br />
class (Monad m) => Stream m h | h->m where<br />
vPutStrLn :: h -> String -> m ()<br />
vGetContents :: h -> m String<br />
vIsEOF :: h -> m Bool<br />
vClose :: h -> m ()<br />
....................<br />
<br />
This means that you already know how to use any stream! The Stream interface<br />
currently has 8 implementations: a Handle itself, raw files, pipes,<br />
memory buffers and string buffers. Future plans include support for<br />
memory-mapped files, sockets, circular memory buffers for interprocess<br />
communication and UArray-based streams.<br />
<br />
By themselves, these Stream implementations are rather simple. Basically,<br />
to implement new Stream type, it's enough to provide vPutBuf/vGetBuf<br />
operations, or even vGetChar/vPutChar. The latter way, although <br />
inefficient, allows us to implement streams that can work in any monad.<br />
StringReader and StringBuffer streams use this to provide string-based<br />
Stream class implementations both for IO and ST monads. Yes, you can<br />
use the full power of Stream operations inside the ST monad!<br />
<br />
=== Layers of Functionality ===<br />
<br />
All additional functionality is implemented via Stream Transformers,<br />
which are just parameterized Streams, whose parameters also<br />
implement the Stream interface. This allows you to apply any number of stream<br />
transformers to the raw stream and then use the result as an ordinary<br />
Stream. For example:<br />
<br />
h <- openRawFD "test" WriteMode<br />
>>= bufferBlockStream<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
<br />
This code creates a new FD, which represents a raw file, and then adds<br />
to this Stream buffering, Char encoding and locking functionality. The<br />
result type of "h" is something like this:<br />
<br />
WithLocking (WithEncoding (BufferedBlockStream FD))<br />
<br />
The complete type, as well as all the intermediate types, implements the Stream<br />
interface. Each transformer intercepts operations corresponding to its<br />
nature, and passes the rest through. For example, the encoding transformer<br />
intercepts only vGetChar/vPutChar operations and translates them to<br />
the sequences of vGetByte/vPutByte calls of the lower-level stream.<br />
The locking transformer just wraps any operation in the locking wrapper.<br />
<br />
We can trace, for example, the execution of a "vPutBuf" operation on the<br />
above-constructed Stream. First, the locking transformer acquires a lock<br />
and then passes this call to the next level. Then the encoding transformer does<br />
nothing and passes this call to the next level. The buffering<br />
transformer flushes the current buffer and passes the call further.<br />
Finally, FD itself performs the operation after all these<br />
preparations and on the returning path, the locking transformer release<br />
its lock.<br />
<br />
As another example, the "vPutChar" call on this Stream is<br />
transformed (after locking) into several "vPutByte" calls by the<br />
encoding transformer, and these bytes go to the buffer in the<br />
buffering transformer, with or without a subsequent call to the FD's<br />
"vPutBuf".<br />
<br />
=== Modularity ===<br />
<br />
As you can see, stream transformers really are independent of each<br />
other. This allows you to use them on any stream and in any combination<br />
(but you should apply them in proper order - buffering, then Char<br />
encoding, then locking). As a result, you can apply to the stream<br />
only the transformers that you really need. If you don't use the<br />
stream in multiple threads, you don't need to apply the locking<br />
transformer. If you don't use any encodings other than Latin-1 -- or<br />
don't use text I/O at all -- you don't need an encoding transformer.<br />
Moreover, you may not even need to know anything about the UserData transformer<br />
until you actually need to use it :)<br />
<br />
Both streams and stream transformers can be implemented by 3rd-party<br />
libraries. Streams and transformers from arbitrary libraries will<br />
seamlessly work together as long as they properly implement the Stream<br />
interface. My future plans include implementation of an on-the-fly<br />
(de)compression transformer and I will be happy to see 3rd-party<br />
transformers that intercept vGetBuf/vPutBuf calls and use select(),<br />
kqueue() and other methods to overlap I/O operations.<br />
<br />
=== Speed ===<br />
<br />
A quick comment about speed: it's fast enough -- 10-50 MB/s (depending<br />
on the type of operation) on a 1GHz cpu. The Handle operations, for comparison, show speed of 1-10 mb/s on the same computer. But that don't means that each and any operation in new library is 10 times faster. Strict I/O (including vGetChar/vPutChar) is a LOT faster. I included a demonstration of this fascinating speed as "Examples/wc.hs". If you need a really high speed, don't forget to increase buffer size with "vSetBuffering".<br />
<br />
On the other side, lazy I/O (including any operations that receive or return strings) show only modest speedup. This is limited by Haskell/GHC itself and I can't do much to get around these limits. Instead, I plan to provide support for I/O using packed strings. This will allow to write I/O-intensive Haskell programs that are as fast as their C counterparts.<br />
<br />
The library includes benchmarking code in the file "Examples/StreamsBenchmark.hs"<br />
<br />
=== Stage of Development ===<br />
<br />
The library is currently at the beta stage. It contains a number of<br />
known minor problems and an unknown number of yet-to-be-discovered bugs.<br />
It is not properly documented, doesn't include QuickCheck tests, is not<br />
cabalized, and not all "h*" operations have their "v*" equivalents yet.<br />
If anyone wants to join this effort in order to help fix these oddities<br />
and prepare the lib for inclusion in the standard libraries suite, I would<br />
be really happy. :) I will also be happy (although much less ;) to see<br />
bug reports and suggestions about its interface and internal<br />
organization. It's just a first public version, so we still can change<br />
everything here!<br />
<br />
Main disadvantage of the library is that it supports only Hugs and GHC<br />
because of using extensions in type classe system. I think that it<br />
can be made H98-compatible at the cost of excluding support for non-IO<br />
monads. I will try to make such a stripped version for other compilers<br />
if people are interested.<br />
<br />
== Overview of Stream Transformers ==<br />
<br />
=== Buffering ===<br />
<br />
There are three buffering transformers. Each buffering transformer<br />
implements support for vGetByte, vPutChar, vGetContents and other<br />
byte- and text-oriented operations for the streams, which by themselves<br />
support only vGetBuf/vPutBuf (or vReceiveBuf/vSendBuf) operations.<br />
<br />
The first transformer can be applied to any stream supporting<br />
vGetBuf/vPutBuf. This is applied by the operation "bufferBlockStream". The<br />
well-known vSetBuffering/vGetBuffering operations are intercepted by<br />
this transformer and used to control buffer size. At this moment, only<br />
BlockBuffering is implemented, while LineBuffering and NoBuffering are<br />
only in the planning stages.<br />
<br />
Two other transformers can be applied to streams that implement<br />
vReceiveBuf/vSendBuf operations -- that is, streams whose data<br />
reside in memory, including in-memory streams and memory-mapped<br />
files. In these cases, the buffering transformer doesn't need to allocate<br />
a buffer itself, it just requests from the underlying stream the address and<br />
size of the next available portion of data. Nevertheless, the final<br />
result is the same -- we get support for all byte- and text-oriented<br />
I/O operations. The "bufferMemoryStream" operation can be applied to any<br />
memory-based stream to add buffering to it. The "bufferMemoryStreamUnchecked" operation (which implements the third buffering<br />
transformer) can be used instead, if you can guarantee that I/O<br />
operations can't overflow the used buffer.<br />
<br />
=== Encoding ===<br />
<br />
The Char encoding transformer allows you to encode each Char written to the<br />
stream as a sequence of bytes, implementing UTF and other encodings.<br />
This transformer can be applied to any stream implementing<br />
vGetByte/vPutByte operations and in return it implements<br />
vGetChar/vPutChar and all other text-oriented operations. This<br />
transformer can be applied to a stream with the "withEncoding encoding"<br />
operation, where `encoding` may be `latin1`, `utf8` or any other<br />
encoding that you (or a 3rd-party lib) implement. Look at the<br />
"Data.CharEncoding" module to see how to implement new encodings.<br />
Encoding of streams created with the "withEncoding" operation can be<br />
changed at any moment with "vSetEncoding" and queried with<br />
"vGetEncoding". See examples of their usage in the file<br />
"Examples/CharEncoding.hs"<br />
<br />
=== Locking ===<br />
<br />
The locking transformer ensures that the stream is properly shared by <br />
several threads. You already know enough about its basic usage --<br />
"withLocking" applies this transformer to the stream and all the<br />
required locking is performed automagically. You can also use "lock"<br />
operations to acquire the lock explicitly during multiple operations:<br />
<br />
lock h $ \h -> do<br />
savedpos <- vTell h<br />
vSeek h AbsoluteSeek 100<br />
vPutStr h ":-)"<br />
vSeek h AbsoluteSeek savedpos<br />
<br />
See the file "Examples/Locking.hs" for examples of using locking transformer.<br />
<br />
=== Attaching user data ===<br />
<br />
This transformer allows you to attach arbitrary data to any Stream. It does nothing extraordinary except that the stream with attached data is the proper Stream, again. See example of its usage in the file "Examples/UserData.hs" <br />
<br />
== Overview of Stream Types ==<br />
<br />
=== Handle (legacy way to access files/sockets) ===<br />
<br />
"Handle" is an instance of the Stream class, with a straightforward implementation. You can use the<br />
Char encoding transformer with Handles. Although Handles implement<br />
buffering and locking by themselves, you may also be interested in<br />
applying these transformers to the Handle type. This has<br />
benefits -- "bufferBlockStream" works faster than internal Handle<br />
buffering, and the locking transformer enables the use of a "lock" operation to<br />
create a lock around a sequence of operations. Moreover, the locking<br />
transformer should be used to ensure proper multi-threading operation<br />
of Handle with added encoding or buffering facilities.<br />
<br />
=== FD (new way to access files) ===<br />
<br />
The new method of using files, independent of the existing I/O<br />
library, is implemented with the FD type. FD is just an Int representing a<br />
POSIX file descriptor and the FD type implements only basic Stream I/O<br />
operations - vGetBuf and vPutBuf. So, to create a full-featured FD-based<br />
stream, you need to apply buffering transformers. Therefore, the library<br />
defines two ways to open files with FD - openRawFD/openRawBinaryFD<br />
just creates FD, while openFD/openBinaryFD creates FD and immediatelly<br />
apply buffering transformer (bufferBlockStream) to it. In most cases<br />
you will use the latter operations. Both pairs mimic the arguments and<br />
behaviour of well-known Handle operations openFile/openBinaryFile, so<br />
you already know how to use them. Other transformers may be used then<br />
as you need. So, abovementioned example can be abbreviated to:<br />
<br />
h <- openFD "test" WriteMode<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
<br />
Thus, to switch from the existing I/O library to using Streams, you<br />
need only to replace "h" with "v" in the names of Handle operations, and<br />
replace openFile/openBinaryFile calls with openFD/openBinaryFD while<br />
adding the "withLocking" transformer to files used in multiple threads.<br />
That's all!<br />
<br />
File "Examples/FD.hs" will show you the usage of FD to work with files.<br />
<br />
<br />
=== MemBuf (memory-resident stream) ===<br />
<br />
MemBuf is a stream type, that keeps its contents in memory buffer.<br />
There are two types of MemBufs you can create - you can either open<br />
existing memory buffer with "openMemBuf ptr size" or create new one<br />
with "createMemBuf initsize". MemBuf opened by "openMemBuf" will be<br />
never resized or moved in memory, and will not be freed by "vClose".<br />
MemBuf created by "createMemBuf" will grow as needed, can be manually<br />
resized by "vSetFileSize" operation, and is automatically freed by<br />
"vClose".<br />
<br />
Actually, raw MemBufs created by the "createRawMemBuf" and "openRawMemBuf"<br />
operations, while createMemBuf/openMemBuf incorporate an additional<br />
"bufferMemoryStream" call (as you should remember, buffering adds vGetChar,<br />
vPutStr and other text- and byte-I/O operations on top of vReceiveBuf<br />
and vSendBuf). You can also apply Char encoding and locking<br />
transformers to these streams. The "saveToFile" and "readFromFile" operations provide an easy way to save/restore buffer contents in a file. <br />
<br />
File "Examples/MemBuf.hs" demonstrates the usage of MemBuf.<br />
<br />
=== FunctionsMemoryStream ===<br />
<br />
This Stream type allows implementation of arbitrary streams, just by<br />
providing three functions that implement vReceiveBuf, vSendBuf and cleanup<br />
operations. It seems that this Stream type is of interest only for my<br />
own program and can be scrutinized only as example of creating 3rd-party<br />
Stream types. It is named "FunctionsMemoryStream", see the sources if you<br />
are interested.<br />
<br />
=== StringReader & StringBuffer (String-based streams) ===<br />
<br />
Four remaining Stream types are part of the HVIO module and I copied their<br />
description from there:<br />
<br />
In addition to Handle, there are several pre-defined stream types for<br />
your use. 'StringReader' is a particularly interesting one. At<br />
creation time, you pass it a String. Its contents are read lazily<br />
whenever a read call is made. It can be used, therefore, to implement<br />
filters (simply initialize it with the result from, say, a map over<br />
hGetContents from another Stream object), codecs, and simple I/O<br />
testing. Because it is lazy, it needs not hold the entire string in<br />
memory. You can create a 'StringReader' with a call to<br />
'newStringReader'.<br />
<br />
'StringBuffer' is a similar type, but with a different purpose. It<br />
provides a full interface like Handle (it supports read, write and<br />
seek operations). However, it maintains an in-memory buffer with the<br />
contents of the file, rather than an actual on-disk file. You can<br />
access the entire contents of this buffer at any time. This can be<br />
quite useful for testing I/O code, or for cases where existing APIs<br />
use I/O, but you prefer a String representation. Note however that<br />
this stream type is very inefficient. You can create a 'StringBuffer'<br />
with a call to 'newStringBuffer'.<br />
<br />
One significant improvement over the original HVIO library is that<br />
'StringReader' and 'StringBuffer' can work not only in IO, but also in<br />
ST monad.<br />
<br />
=== Pipes (passing data between Haskell threads) ===<br />
<br />
Finally, there are pipes. These pipes are analogous to the Unix pipes<br />
that are available from System.Posix, but don't require Unix and work<br />
only in Haskell. When you create a pipe, you actually get two Stream<br />
objects: a 'PipeReader' and a 'PipeWriter'. You must use the<br />
'PipeWriter' in one thread and the 'PipeReader' in another thread.<br />
Data that's written to the 'PipeWriter' will then be available for<br />
reading with the 'PipeReader'. The pipes are implemented completely<br />
with existing Haskell threading primitives, and require no special<br />
operating system support. Unlike Unix pipes, these pipes cannot be<br />
used across a fork(). Also unlike Unix pipes, these pipes are<br />
portable and interact well with Haskell threads. A new pipe can be<br />
created with a call to 'newHVIOPipe'.</div>HenkJanVanTuylhttps://wiki.haskell.org/index.php?title=Library/Streams&diff=2188Library/Streams2006-02-05T19:15:31Z<p>HenkJanVanTuyl: </p>
<hr />
<div>== Introduction ==<br />
<br />
=== Streams: the extensible I/O library ===<br />
<br />
I have developed a new I/O library that IMHO is so sharp that it can<br />
eventually replace the current I/O facilities based on using Handles.<br />
The main advantage of the new library is its strong modular design using<br />
typeclasses. The library consists of small independent modules, each<br />
implementing one type of stream (file, memory buffer, pipe) or one<br />
part of common stream functionality (buffering, Char encoding,<br />
locking). 3rd-party libs can easily add new stream types and new common<br />
functionality. Other benefits of the new library include support for<br />
streams functioning in any monad, Hugs and GHC compatibility, high<br />
speed and an easy migration path from the existing I/O library.<br />
<br />
The Streams library is heavily based on the HVIO module written by John<br />
Goerzen. I especially want to thank John for his clever design and<br />
implementation. Really, I just renamed HVIO to Stream and presented<br />
this as my own work. :) Further development direction was inspired<br />
by the "New I/O library" written by Simon Marlow.<br />
<br />
=== Simple Streams ===<br />
<br />
The key concept of the lib is the Stream class, whose interface mimics<br />
familiar interface for Handles, just with "h" replaced with "v" in<br />
function names:<br />
<br />
class (Monad m) => Stream m h | h->m where<br />
vPutStrLn :: h -> String -> m ()<br />
vGetContents :: h -> m String<br />
vIsEOF :: h -> m Bool<br />
vClose :: h -> m ()<br />
....................<br />
<br />
This means that you already know how to use any stream! The Stream interface<br />
currently has 8 implementations: a Handle itself, raw files, pipes,<br />
memory buffers and string buffers. Future plans include support for<br />
memory-mapped files, sockets, circular memory buffers for interprocess<br />
communication and UArray-based streams.<br />
<br />
By themselves, these Stream implementations are rather simple. Basically,<br />
to implement new Stream type, it's enough to provide vPutBuf/vGetBuf<br />
operations, or even vGetChar/vPutChar. The latter way, although <br />
inefficient, allows us to implement streams that can work in any monad.<br />
StringReader and StringBuffer streams use this to provide string-based<br />
Stream class implementations both for IO and ST monads. Yes, you can<br />
use the full power of Stream operations inside the ST monad!<br />
<br />
=== Layers of Functionality ===<br />
<br />
All additional functionality is implemented via Stream Transformers,<br />
which are just parameterized Streams, whose parameters also<br />
implement the Stream interface. This allows you to apply any number of stream<br />
transformers to the raw stream and then use the result as an ordinary<br />
Stream. For example:<br />
<br />
h <- openRawFD "test" WriteMode<br />
>>= bufferBlockStream<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
<br />
This code creates a new FD, which represents a raw file, and then adds<br />
to this Stream buffering, Char encoding and locking functionality. The<br />
result type of "h" is something like this:<br />
<br />
WithLocking (WithEncoding (BufferedBlockStream FD))<br />
<br />
The complete type, as well as all the intermediate types, implements the Stream<br />
interface. Each transformer intercepts operations corresponding to its<br />
nature, and passes the rest through. For example, the encoding transformer<br />
intercepts only vGetChar/vPutChar operations and translates them to<br />
the sequences of vGetByte/vPutByte calls of the lower-level stream.<br />
The locking transformer just wraps any operation in the locking wrapper.<br />
<br />
We can trace, for example, the execution of a "vPutBuf" operation on the<br />
above-constructed Stream. First, the locking transformer acquires a lock<br />
and then passes this call to the next level. Then the encoding transformer does<br />
nothing and passes this call to the next level. The buffering<br />
transformer flushes the current buffer and passes the call further.<br />
Finally, FD itself performs the operation after all these<br />
preparations and on the returning path, the locking transformer release<br />
its lock.<br />
<br />
As another example, the "vPutChar" call on this Stream is<br />
transformed (after locking) into several "vPutByte" calls by the<br />
encoding transformer, and these bytes go to the buffer in the<br />
buffering transformer, with or without a subsequent call to the FD's<br />
"vPutBuf".<br />
<br />
=== Modularity ===<br />
<br />
As you can see, stream transformers really are independent of each<br />
other. This allows you to use them on any stream and in any combination<br />
(but you should apply them in proper order - buffering, then Char<br />
encoding, then locking). As a result, you can apply to the stream<br />
only the transformers that you really need. If you don't use the<br />
stream in multiple threads, you don't need to apply the locking<br />
transformer. If you don't use any encodings other than Latin-1 -- or<br />
don't use text I/O at all -- you don't need an encoding transformer.<br />
Moreover, you may not even need to know anything about the UserData transformer<br />
until you actually need to use it :)<br />
<br />
Both streams and stream transformers can be implemented by 3rd-party<br />
libraries. Streams and transformers from arbitrary libraries will<br />
seamlessly work together as long as they properly implement the Stream<br />
interface. My future plans include implementation of an on-the-fly<br />
(de)compression transformer and I will be happy to see 3rd-party<br />
transformers that intercept vGetBuf/vPutBuf calls and use select(),<br />
kqueue() and other methods to overlap I/O operations.<br />
<br />
=== Speed ===<br />
<br />
A quick comment about speed: it's fast enough -- 10-50 MB/s (depending<br />
on the type of operation) on a 1GHz cpu. The Handle operations, for comparison, show speed of 1-10 mb/s on the same computer. But that don't means that each and any operation in new library is 10 times faster. Strict I/O (including vGetChar/vPutChar) is a LOT faster. I included a demonstration of this fascinating speed as "Examples/wc.hs". If you need a really high speed, don't forget to increase buffer size with "vSetBuffering".<br />
<br />
On the other side, lazy I/O (including any operations that receive or return strings) show only modest speedup. This is limited by Haskell/GHC itself and I can't do much to get around these limits. Instead, I plan to provide support for I/O using packed strings. This will allow to write I/O-intensive Haskell programs that are as fast as their C counterparts.<br />
<br />
The library includes benchmarking code in the file "Examples/StreamsBenchmark.hs"<br />
<br />
=== Stage of Development ===<br />
<br />
The library is currently at the beta stage. It contains a number of<br />
known minor problems and an unknown number of yet-to-be-discovered bugs.<br />
It is not properly documented, doesn't include QuickCheck tests, is not<br />
cabalized, and not all "h*" operations still have their "v*" equivalents.<br />
If anyone wants to join this effort in order to help fix these oddities<br />
and prepare the lib for inclusion in the standard libraries suite, I would<br />
be really happy. :) I will also be happy (although much less ;) to see<br />
bug reports and suggestions about its interface and internal<br />
organization. It's just a first public version, so we still can change<br />
everything here!<br />
<br />
Main disadvantage of the library is that it supports only Hugs and GHC<br />
because of using extensions in type classe system. I think that it<br />
can be made H98-compatible at cost of excluding support for non-IO<br />
monads. I will try to make such a stripped version for other compilers<br />
if people are interested.<br />
<br />
== Overview of Stream Transformers ==<br />
<br />
=== Buffering ===<br />
<br />
There are 3 buffering transformers. Each buffering transformer<br />
implements support for vGetByte, vPutChar, vGetContents and other<br />
byte- and text-oriented operations for the streams, which by themselves<br />
support only vGetBuf/vPutBuf (or vReceiveBuf/vSendBuf) operations.<br />
<br />
The first transformer can be applied to any stream supporting<br />
vGetBuf/vPutBuf. This is applied by the operation "bufferBlockStream". The<br />
well-known vSetBuffering/vGetBuffering operations are intercepted by<br />
this transformer and used to control buffer size. At this moment, only<br />
BlockBuffering is implemented, while LineBuffering and NoBuffering are<br />
only in the planning stages.<br />
<br />
Two other transformers can be applied to streams that implement<br />
vReceiveBuf/vSendBuf operations -- that is, streams whose data<br />
resides in memory, including in-memory streams and memory-mapped<br />
files. In these cases, the buffering transformer doesn't need to allocate<br />
a buffer itself, it just requests from the underlying stream the address and<br />
size of the next available portion of data. Nevertheless, the final<br />
result is the same -- we get support for all byte- and text-oriented<br />
I/O operations. The "bufferMemoryStream" operation can be applied to any<br />
memory-based stream to add buffering to it. The "bufferMemoryStreamUnchecked" operation (which implements the third buffering<br />
transformer) can be used instead, if you can guarantee that I/O<br />
operations can't overflow used buffer.<br />
<br />
=== Encoding ===<br />
<br />
The Char encoding transformer allows you to encode each Char written to the<br />
stream as a sequence of bytes, implementing UTF and other encodings.<br />
This transformer can be applied to any stream implementing<br />
vGetByte/vPutByte operations and in return it implements<br />
vGetChar/vPutChar and all other text-oriented operations. This<br />
transformer can be applied to a stream with the "withEncoding encoding"<br />
operation, where `encoding` may be `latin1`, `utf8` or any other<br />
encoding that you (or a 3rd-party lib) implement. Look at the<br />
"Data.CharEncoding" module to see how to implement new encodings.<br />
Encoding of streams created with the "withEncoding" operation can be<br />
changed at any moment with "vSetEncoding" and queried with<br />
"vGetEncoding". See examples of their usage in the file<br />
"Examples/CharEncoding.hs"<br />
<br />
=== Locking ===<br />
<br />
The locking transformer ensures that the stream is properly shared by <br />
several threads. You already know enough about its basic usage --<br />
"withLocking" applies this transformer to the stream and all the<br />
required locking is performed automagically. You can also use "lock"<br />
operations to acquire the lock explicitly during multiple operations:<br />
<br />
lock h $ \h -> do<br />
savedpos <- vTell h<br />
vSeek h AbsoluteSeek 100<br />
vPutStr h ":-)"<br />
vSeek h AbsoluteSeek savedpos<br />
<br />
See the file "Examples/Locking.hs" for examples of using locking transformer.<br />
<br />
=== Attaching user data ===<br />
<br />
This transformer allows you to attach arbitrary data to any Stream. It does nothing extraordinary except that the stream with attached data is the proper Stream, again. See example of its usage in the file "Examples/UserData.hs" <br />
<br />
== Overview of Stream Types ==<br />
<br />
=== Handle (legacy way to access files/sockets) ===<br />
<br />
Handle is an instance of the Stream class, with a straightforward implementation. You can use the<br />
Char encoding transformer with Handles. Although Handles implement<br />
buffering and locking by themselves, you may also be interested in<br />
applying these transformers to the Handle type. This has<br />
benefits -- "bufferBlockStream" works faster than internal Handle<br />
buffering, and the locking transformer enables the use of a "lock" operation to<br />
create a lock around a sequence of operations. Moreover, the locking<br />
transformer should be used to ensure proper multi-threading operation<br />
of Handle with added encoding or buffering facilities.<br />
<br />
=== FD (new way to access files) ===<br />
<br />
The new method of using files, independent of the existing I/O<br />
library, is implemented with the FD type. FD is just an Int representing a<br />
POSIX file descriptor and FD type implements only basic Stream I/O<br />
operations - vGetBuf and vPutBuf. So, to create a full-featured FD-based<br />
stream, you need to apply buffering transformers. Therefore, library<br />
defines two ways to open files with FD - openRawFD/openRawBinaryFD<br />
just creates FD, while openFD/openBinaryFD creates FD and immediatelly<br />
apply buffering transformer (bufferBlockStream) to it. In most cases<br />
you will use the later operations. Both pairs mimics the arguments and<br />
behaviour of well-known Handle operations openFile/openBinaryFile, so<br />
you already know how to use them. Other transformers may be used then<br />
as you need. So, abovementioned example can be abbreviated to:<br />
<br />
h <- openFD "test" WriteMode<br />
>>= withEncoding utf8<br />
>>= withLocking<br />
<br />
Thus, to switch from the existing I/O library to using Streams, you<br />
need only to replace "h" with "v" in names of Handle operations, and<br />
replace openFile/openBinaryFile calls with openFD/openBinaryFD while<br />
adding "withLocking" transformer to files used in multiple threads.<br />
That's all!<br />
<br />
File "Examples/FD.hs" will show you the usage of FD to work with files.<br />
<br />
<br />
=== MemBuf (memory-resident stream) ===<br />
<br />
MemBuf is a stream type, that keeps its contents in memory buffer.<br />
There are two types of MemBufs you can create - you can either open<br />
existing memory buffer with "openMemBuf ptr size" or create new one<br />
with "createMemBuf initsize". MemBuf opened by "openMemBuf" will be<br />
never resized or moved in memory, and will not be freed by "vClose".<br />
MemBuf created by "createMemBuf" will grow as needed, can be manually<br />
resized by "vSetFileSize" operation, and is automatically freed by<br />
"vClose".<br />
<br />
Actually, raw MemBufs created by the "createRawMemBuf" and "openRawMemBuf"<br />
operations, while createMemBuf/openMemBuf incorporates additional<br />
"bufferMemoryStream" call (as you should remember, buffering adds vGetChar,<br />
vPutStr and other text- and byte-i/o operations on top of vReceiveBuf<br />
and vSendBuf). You can also apply Char encoding and locking<br />
transformers to these streams. The "saveToFile" and "readFromFile" operations provide easy way to save/restore buffer contents in a file. <br />
<br />
File "Examples/MemBuf.hs" demonstrates usage of MemBuf.<br />
<br />
=== FunctionsMemoryStream ===<br />
<br />
This Stream type allow to implement arbitrary streams just by<br />
providing 3 functions that implement vReceiveBuf, vSendBuf and cleanup<br />
operations. It seems that this Stream type is of interest only for my<br />
own program and can be scrutinized only as example of creating 3-party<br />
Stream types. It named "FunctionsMemoryStream", see the sources if you<br />
are interested.<br />
<br />
=== StringReader & StringBuffer (String-based streams) ===<br />
<br />
Four remaining Stream types was a part of HVIO module and I copy their<br />
description from there:<br />
<br />
In addition to Handle, there are several pre-defined stream types for<br />
your use. 'StringReader' is a particularly interesting one. At<br />
creation time, you pass it a String. Its contents are read lazily<br />
whenever a read call is made. It can be used, therefore, to implement<br />
filters (simply initialize it with the result from, say, a map over<br />
hGetContents from another Stream object), codecs, and simple I/O<br />
testing. Because it is lazy, it need not hold the entire string in<br />
memory. You can create a 'StringReader' with a call to<br />
'newStringReader'.<br />
<br />
'StringBuffer' is a similar type, but with a different purpose. It<br />
provides a full interface like Handle (it supports read, write and<br />
seek operations). However, it maintains an in-memory buffer with the<br />
contents of the file, rather than an actual on-disk file. You can<br />
access the entire contents of this buffer at any time. This can be<br />
quite useful for testing I/O code, or for cases where existing APIs<br />
use I/O, but you prefer a String representation. Note however that<br />
this stream type is very inefficient. You can create a 'StringBuffer'<br />
with a call to 'newStringBuffer'.<br />
<br />
One significant improvement over the original HVIO library is that<br />
'StringReader' and 'StringBuffer' can work not only in IO, but also in<br />
ST monad.<br />
<br />
=== Pipes (passing data between Haskell threads) ===<br />
<br />
Finally, there are pipes. These pipes are analogous to the Unix pipes<br />
that are available from System.Posix, but don't require Unix and work<br />
only in Haskell. When you create a pipe, you actually get two Stream<br />
objects: a 'PipeReader' and a 'PipeWriter'. You must use the<br />
'PipeWriter' in one thread and the 'PipeReader' in another thread.<br />
Data that's written to the 'PipeWriter' will then be available for<br />
reading with the 'PipeReader'. The pipes are implemented completely<br />
with existing Haskell threading primitives, and require no special<br />
operating system support. Unlike Unix pipes, these pipes cannot be<br />
used across a fork(). Also unlike Unix pipes, these pipes are<br />
portable and interact well with Haskell threads. A new pipe can be<br />
created with a call to 'newHVIOPipe'.</div>HenkJanVanTuyl