summaryrefslogtreecommitdiff
path: root/ml
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2007-12-28 06:52:01 +0000
committermichele.simionato <devnull@localhost>2007-12-28 06:52:01 +0000
commite7db1ad5c6211663feb109257fcd668320e6bb00 (patch)
tree0086d2315a62e7f79cf4fd2846efe90ac24d88f6 /ml
parentf1d1e44a9c0923dc666dc5540f226e1943ed0b37 (diff)
downloadmicheles-e7db1ad5c6211663feb109257fcd668320e6bb00.tar.gz
Various changes
Diffstat (limited to 'ml')
-rw-r--r--ml/comb.txt340
-rw-r--r--ml/format.aml8
-rw-r--r--ml/functors.txt67
-rw-r--r--ml/intro.txt438
-rw-r--r--ml/text.txt318
-rw-r--r--ml/types.txt158
6 files changed, 724 insertions, 605 deletions
diff --git a/ml/comb.txt b/ml/comb.txt
new file mode 100644
index 0000000..dea4e43
--- /dev/null
+++ b/ml/comb.txt
@@ -0,0 +1,340 @@
+Functional Programming For Dynamic Programmers - Part 4
+=======================================================
+
+:author: Michele Simionato
+:date: December 2007
+
+This is the fourth of a series of articles on functional programming in
+statically typed languages. It is intended for dynamic programmers, i.e.
+for programmers with a background in dynamically typed languages, such as Perl,
+Python, Ruby, or languages in the Lisp family. The approch is eminently practical
+and example-based; the main goal is to see if we can stole some good idea from
+statically typed languages. In order to be concrete, I will consider languages
+in the ML family, because they are pretty nice and much easier to understand
+that Haskell.
+
+Academic languages vs enterprise languages
+-----------------------------------------------------------
+
+There is a widespread misconception that academic languages are clean but
+unpractical, whereas enterprise languages are practical but dirty. I never
+bought that argument, since I have seen examples of the opposite, and also
+because I always believed that a language can be both clean *and* practical.
+
+For instance in my opinion both Python and Ruby are reasonably clean and
+reasonably practical and this is part of the reason why they are having so
+much success lately. I believe the reason why they are so clean is that from
+the beginning they managed to stay away from optimization, the root of all evil.
+On the other hand, I am also convinced that it possible to write a language
+which is both practical, clean and fast. Unfortunately, such a language does
+not exist yet. SML is fast and somewhat clean but it is definitely not practical.
+Here by practical I mean enterprise-ready.
+
+There is an irriducible aporia_ between enterprise programmers and
+academics: people working on enterprise are facing every day problems
+that are already solved; to them, the importanting thing is to avoid
+reinventing the wheel. Given two languages, one that provides a solution
+to a problem they have, and one providing the tools to build the solution
+themselves, they prefer the first one. This is natural since in an enterprise
+environment one has deadlines and it is essential to be as productive
+as possible; of course the best way to be productive is to *not write* code,
+and to re-use code written by others.
+On the other hand, in academia one has to do with new problems, or
+with new techniques to solve old problems: the proof of concept is
+more important than the practical implementation. Also, academics
+are (rightly) interested in writing papers, not in writing code. For all these
+reasons it is clear that you cannot face an
+academic language such as SML with an enterprise mindset.
+For instance, if you come from an enterprise environment you will be used to
+expect ready availabity of a nearly infinite number
+of libraries doing everything you may need and more. This is certainly
+the situation for all enterprise-level languages such as C, C++, Java, C#,
+Perl, Python and now even Ruby, which is younger but rapidly spreading.
+
+As I said many time, I assume that my readers are dynamic programmers,
+well acquainted with scripting languages; I feel the urge warn them that the
+situation in SML is very different than in Perl, Python or Ruby [#]_ . Here
+are a few differences.
+
+1.
+ Scripting languages have a dominant (sometimes unique)
+ implementation; ML has dozens of implementations, each with
+ useful features which are not in the standard, so that
+ different implementations provides incompatible extensions.
+
+2.
+ Scripting languages have a BDFL_ (Benevolent Dictator For Life,
+ i.e. Guido van Rossum for Python, Larry Wall for Perl, Yukihiro
+ Matsumoto for Ruby) whereas SML is a language designed by
+ committee. That means that a scripting language can evolve much
+ *much* faster than a language designed by committee. Also,
+ languages designed by committee are compromises whereas
+ single-person languages are much more consistent.
+
+3.
+ SML has a very small community: the support you can have in newsgroup
+ and mailing lists is simply not comparable to the support you can get in
+ the scripting languages mailing list and newsgroups.
+
+ if Andreas Rossberg won the first price
+ at the lottery and decided to leave for a tropical paradise, nobody would
+ provide support for Alice ML on the mailing list
+
+4.
+ SML is a deserted language, even compared to non-mainstream languages
+ as OCaml, Haskell, Scheme, Common Lisp, Smalltalk, Tcl, ...; it is perhaps the
+ least used language I have ever seen and this is a pity.
+
+5.
+ Generally speaking, if you want bindings for external libraries you have to write
+ it yourself; there are no standard bindings for GUI toolkits, database drivers,
+ scientific programming libraries.
+
+6.
+ Scripting languages have traditionally a strong support for Web programming
+ which is next to absent in SML.
+
+
+All the issues I have just listed are just accidental, not
+structural: in principle one could just solve them by writing code; in principle
+a DHH_ could take an SML implementation and make it the next Ruby on Rails.
+However that has not happened yet, so if you want to work with SML right now you
+will be forced to reinvent the wheel. In this article I will reinvent a few wheels,
+just to be able to give some meaninful example to the enterprise programmer
+in the next articles.
+
+.. _aporia: http://en.wikipedia.org/wiki/Aporia
+.. _BDFL: http://en.wikipedia.org/wiki/BDFL
+.. _DHH: http://en.wikipedia.org/wiki/DHH
+
+.. [#] Notice that I put Perl, Python and Ruby in the mainstream
+ languages, since even if the number of dynamic programmers is
+ inferior the number of say C/C++, Java or .NET programmers,
+ in practice scripting programmers have available nearly everything the
+ other languages have available, and much better languages.
+
+String interpolation the practical way
+--------------------------------------------------------
+
+Every language has some builtin mechanism to do string interpolation: C
+has ``sprintf``, Python has the ``%`` operator, Lisp has ``format``, but
+SML has none. Therefore I will provide here a very simple interpolation library
+for SML, so that I will be able to provide more interesting examples later.
+The library provides a single higher order function
+``format: string -> string list -> string`` converting a template
+string into a 'string list -> string' function replacing the template
+with the provided list of arguments. For instance
+
+ ``format "The square of $ is $\n" ["3", "9"]``
+
+returns
+
+ ``"The square of 3 is 9"``
+
+For sake of simplicity, we will not implement any mechanism to escape
+dollar signs, so if the format strings contains dollar signs for other
+reasons (for instance there is money involved) the library will not
+work.
+
+The library is heavily based on list processing.
+Lists are the most common data structures in
+functional languages (they formed the core of Lisp at its beginning) .
+You should look at the `basis library`_
+to see what you can do with lists. Here I will just say that SML lists are
+linked lists in the same sense of Lisp or Scheme (with the difference
+that they are immutable) and not as in Python (Python lists are actually arrays,
+Python is abusing the name). Just as in Scheme, where
+
+ ``(list 1 2)``
+
+is a shortcut for
+
+ ``(cons 1 (cons 2 '()))``
+
+in ML
+
+ ``[1, 2]``
+
+is a shortcut for
+
+ ``1::2::[]``
+
+and ``::`` is the *cons* operator (short for constructor).
+If you don't know Scheme, a SML lists should be thought of as of a
+nested Python list, i.e.
+
+ ``[1, 2] (SML) => [1, [2, []]] (Python)``
+
+.. _basis library: http://www.standardml.org/Basis/list.html
+
+The core of the algorithm used in the interpolation library is a recursive function
+which takes a list of N+1 terms ``t_1 ... t_{N+1}`` and a list of N arguments
+``a_1 .. a_N`` and returns the string obtained by interspersing the terms
+with the arguments,
+
+ ``t_1 ^ a_1 ^ ... _tN ^ a_N ^ t_{N+1}``
+
+For instance, we will be interspersing ``["The square of ", " is ", "\n"]`` with
+ ``["3", "9"]``.
+
+Here is the code::
+
+ $ cat format.aml
+
+.. include:: format.aml
+ :literal:
+
+A few comments are in order.
+
+1.
+ We used the ability to define exceptions with a value:
+ ``exception ArityError of string`` means that ``ArityError`` accepts
+ a string argument (the error message).
+
+2.
+ We used the standard library ``String.fields`` utility, which splits a string
+ according to a delimiter; in our case ``String.fields (fn c => c = #"$") templ``
+ splits the template into a list a strings using the character ``$`` as delimiter.
+ In SML characters are specified with a sharp notation, so the character ``$``
+ is written ``#"$"``.
+
+3.
+ The builtin ``concat`` concatenates a list of strings whereas ``rev`` reverts
+ a list; you are advised to read the documentation or any book on SML for more.
+
+String interpolation the SML way
+-------------------------------------------------
+
+Functional languages have a reputation for abstractness
+but in this series of articles I have focused solely on very concrete
+earth-bound aspects. To correct the balance, in this section I will discuss
+a few programming techniques which are quite common in
+functional languages, but unheard of in "regular" languages, and that
+require a mathematically-inclined mind to be understood. I am doing so
+because these tricks are actually used a lot in functional languages, and
+I don't want to give a false impression by ignoring them
+and just discussing the easy things.
+
+For instance, you should not believe that functional
+programming is just about functions; there is an higher order
+approach to functional programming, in which the primary objects
+are not functions, but operators operating on functions, the so
+called *combinators*. For people with a background in Physics, I will
+say this is exactly analogous to the switch from the
+Schroedinger picture, in which the emphasis is on the wave functions,
+to the Heisenberg picture, in which the emphasis is on the quantum
+operators.
+
+Having warned my readers that this section is not for the faint of heart
+(but you may safely skip it if you don't feel adventurous)
+I will start from the concatenation operator
+
+::
+
+ ^ : string * string -> string
+
+which satifies the properties::
+
+ a ^ (b ^ c) = (a ^ b) ^ c = a ^ b ^ c
+ a ^ "" = "" ^ a = a
+
+Mathematically speaking, the set of strings in SML
+is a monoid_ with respect to concatenation and the empty
+string "" is the identity element.
+
+.. _monoid: http://en.wikipedia.org/wiki/Monoid
+.. _group: http://en.wikipedia.org/wiki/Group_%28mathematics%29
+.. _group representations: http://en.wikipedia.org/wiki/Group_representation
+
+If you have a background in Mathematics or in Physics you will be familiar
+with the theory of `group representations`_; in particular groups (and monoids
+too) can be represented with operators operating on function spaces.
+In the case at hand, I can define a lift transformation converting plain
+strings into operators by preserving the composition law::
+
+ - fun L s f s' = f (s' ^ s);
+ val L : string -> (string -> 'a) -> string -> 'a = _fn
+
+(this is the same as ``fun L s = fn f => fn s' => f (s' ^ s)``).
+In other words, ``L s`` is an operator (combinator) taking a function and
+returning a function, with the property
+
+ ``L (s1 ^ s2) = (L s1) o (L s2)``
+
+where ``o`` denotes the composition of operators. Just as ``L`` is an upgrade
+operation, promoving a plain simple string to the celestial world of operators
+in function spaces, I can define a downgrade operation ``l``, demoving
+celestial operators back to earthly strings::
+
+ - fun l O = O Fn. id "";
+ val l : (('a -> 'a) -> string -> 'b) -> 'b = _fn
+
+``l`` takes the operator, applies it to the identity function and returns a function
+which is then applied to the empty string, finally bringing back at home a
+plain string; ``l`` is the inverse of ``L``, i.e. ``l o L`` is the identity
+in the space of strings where ``L o l`` is the identity in the space
+of operators. You can try yourself at the prompt that
+
+::
+
+ - l(L"a" o L"b");
+ val it : string = "ab"
+
+so we succeded in making simple things hard and abstract. But not happy with
+that, we are going to make things harder and more abstract, by defining
+another combinator taking a function and returning an higher order function::
+
+ - fun str f s s' = f (s ^ s')
+ val str : (string -> 'a) -> string -> string -> 'a = _fn
+
+(this is the same as ``fun str f = fn s => fn s' => f ( s ^ s')``).
+This combinator is so abstract than even when lift back to the mortal world
+it is more than a mere string, it is actually the identity function on strings::
+
+ - l str;
+ val it : string -> string = _fn
+ - (l str) "hello";
+ val it : string = "hello"
+
+The ``str`` combinator can be composed in the celestial word: when lift
+back to the mortal world, it returns a higher order version of the concatenation
+operator::
+
+ - l (str o str);
+ val it : string -> string -> string = _fn
+
+ - l (str o str) "hello" " world";
+ val it : string = "hello world"
+
+We can also compose ``str`` with other combinators and we can write things like::
+
+ - l( L"a" o str o L"b" o str o L"c") "?" ":";
+ val it : string = "a?b:c"
+
+In other words, we have reinvented string interpolation the difficult way.
+Still, there is at least an advantage of this approach with respect to the
+approach we used in the previous paragraph: combinator-based string interpolation
+happens at compile type and *mistakes are discovered by the compiler*, not
+at runtime. Moreover, the approach can be easily extended to manage
+different types: for instance, if we have to do with numbers, we
+can define the combinators::
+
+ - fun int f s n = f (s ^ (Int.toString n))
+ val str : (string -> 'a) -> string -> string -> 'a = _fn
+
+ - fun real f s n = f (s ^ (Real.toString n))
+ val real : (string -> 'a) -> string -> real -> 'a = _fn
+
+and use them as follows::
+
+ - print (l(int o L" / " o int o L" = " o real o L"\n") 5 2 2.5);
+ 5 / 2 = 2.5
+ val it : unit = ()
+
+If you make a mistake like using an int instead of a real, or if you forget an
+argument, you will get a compile time type error.
+
+----
+
+*Give a man a fish and he will eat for a day. Teach a man to fish and he will eat
+for the rest of his life.* -- Chinese proverb
diff --git a/ml/format.aml b/ml/format.aml
index 1c6427a..6b7acef 100644
--- a/ml/format.aml
+++ b/ml/format.aml
@@ -1,10 +1,8 @@
(*
A library implementing a very simple string interpolation facility.
-It exports a single higher order function 'format' converting a template
-string into a 'string list -> string' function replacing the template
-with the provided list of arguments. An example of usage is
+An example of usage is
-do print(format "The square of $ is $\n" ["3", "9"])
+ print(format "The square of $ is $\n" ["3", "9"])
*)
signature FORMAT = sig
@@ -22,7 +20,7 @@ structure Format = struct
end
val rec interp' =
- fn (t :: [], [], acc) => concat(rev (t :: acc))
+ fn ([t], [], acc) => concat(rev (t :: acc))
| (t :: templ, a :: args, acc) => interp'(templ, args, t :: a :: acc)
| _ => raise ArityError "This should never happen"
diff --git a/ml/functors.txt b/ml/functors.txt
new file mode 100644
index 0000000..8f720c6
--- /dev/null
+++ b/ml/functors.txt
@@ -0,0 +1,67 @@
+Functional Programming For Dynamic Programmers - Part 5
+=======================================================
+
+:author: Michele Simionato
+:date: December 2007
+
+This is the fifth of a series of articles on functional programming in
+statically typed languages. It is intended for dynamic programmers, i.e.
+for programmers with a background in dynamically typed languages, such as Perl,
+Python, Ruby, or languages in the Lisp family. The approch is eminently practical
+and example-based; the main goal is to see if we can stole some good idea from
+statically typed languages. In order to be concrete, I will consider languages
+in the ML family, because they are pretty nice and much easier to understand
+that Haskell.
+
+Input and Output revisited
+--------------------------------------------------------------
+
+In accordance with the example-oriented spirit of this series, I will
+introduce functors with a motivating example, again in the arena of
+input and output.
+We saw in an earlier installament that the standard library provides two
+structures ``TextIO`` and ``BinIO`` for managing text files and binary
+files respectively; we also show that the two structures have many things
+in common, and it is possibile to define a (sub)signature matching both.
+
+
+Structures are not first class objects and
+they cannot be passed to and returned from regular functions.
+To circumvent this restriction, the authors of ML invented
+the concept of *functor*, which is basically a *sui generis* function
+taking structures in input and to returning structures in output. Functors
+are not-first-class objects themselves and therefore they require a specific
+declaration ``functor Name(Struct:SIGNATURE) = funct ... end``.
+
+.. include:: simple-io.aml
+ :literal:
+
+The functor here is important, since it shows how it is possible to write
+generic code in SML. In this case, I have just written a library which is
+able to wrap *any* structure matching the ``SimpleIO`` interface, and I have
+avoided a potentially infinite code duplication.
+
+The specification of which specific structure to use will be the job of the client
+code, not of the library author; in particular a particular user of the library
+may be interested in specializing it both for ``TextIO`` and ``BinIO``
+by writing::
+
+ - structure T = ManagedIO(TextIO)
+ - structure B = ManagedIO(BinIO)
+
+The operation of specializing a functor is also called *functor instantiation*;
+since it happens in a structure declaration it is performed by the compiler
+*at compile time*. The advantage is that the compiler can generate different optimized
+code for the structures ``T`` and ``B`` in the *client* program.
+
+ ``- T.withInputFile "three-lines.txt" (print o T.inputAll)``
+
+----
+
+ *Such things are called individuals because each of them consists
+ of characteristics the collection of which will never be the
+ same for anything else. For the characteristics of Socrates will
+ never be in any other particular. But the characteristics of man —
+ I mean of the man that is general — will be the same in
+ several things, or rather in all particular men insofar as they
+ are men.* -- Porphyry
diff --git a/ml/intro.txt b/ml/intro.txt
index 803bdf6..b47cfc9 100644
--- a/ml/intro.txt
+++ b/ml/intro.txt
@@ -1,5 +1,5 @@
-Functional Programming For Dynamic Programmers - Part 1
-=======================================================
+Functional Programming For Dynamic Programmers - Introduction
+===========================================================================
:author: Michele Simionato
:date: December 2007
@@ -9,73 +9,158 @@ statically typed languages. It is intended for dynamic programmers, i.e.
for programmers with a background in dynamically typed languages, such as Perl,
Python, Ruby, or languages in the Lisp family. The approch is eminently practical
and example-based; the main goal is to see if we can stole some good idea from
-statically typed languages. In order to be concrete, I will consider languages
-in the ML family, because they are pretty nice and much easier to understand
-that Haskell.
+statically typed languages. I will mostly consider languages in the ML family, because
+they are pretty nice and much easier to understand than Haskell.
Declaration of intents
-----------------------
-
-One may rightly wonder why to study functional languages, and in
-particular statically-typed ones such as SML and Haskell: after all,
-their relevance in enterprise programming is negligible, they are practically
-unknown for system
-administration task, and unless you are a computer science researcher
-you have no need to know them. All that is true; on the other hand, if
-you believed it, you would not be a dynamic programmer, you would have
-stick with C and you would not be a reader of my papers.
-A dynamic programmer is the kind of person who, when everbody says
-"the Earth is flat", will wonder: "is the Earth *really* flat?". So,
-even if everybody says that object orientation is good and that
-inheritance is the best thing after sliced bread, a dynamic programmer
-will be suspicious of these claims and will seek for alternatives.
-Exactly in the same way he escaped from the enslaving by static typing
-in old fashioned languages, he will escape from the enslaving by
-dynamic typing languages, if he finds valid alternatives. A dynamic
-programmer has a kind of Casanova complex, and he will never be loyal
-to a single language.
-In particular, a dynamic programmer will judge academic languages
-worth of investigation: he may decided not to use them, but still he may get
-some good ideas from them. After all, Python stole the list comprehension
-idea from Haskell and iterators from Icon; Ruby stole many ideas from Scheme
-and Smalltalk; Perl stole from everybody.
-I always wanted to learn a modern statically-typed functional
-language, at least to have something intelligent to say in the endless
-usenet discussions on dynamic typing vs. static typing; recently, by
-accident, I run into Standard ML (SML, ML stands for Meta Language)
-and I found out it has a very clean syntax, so I decided to learn it [#]_.
-
-SML is practically unknown outside academia (its cousins Caml and F# are a
-little more known, but they are certainly very far away from being
-mainstream languages) and I am not interested in advocating it for use
-in enterprise programming; after all in enterprise programming the technical
-advantages of a language count very little compared to things like the support
-you can get, the ability to find skilled programmers, the interoperability with
-your existent codebase and development environment (I mean the tools and
-libraries you use, the database, etc). My motivation here is to learn something
-from SML, and to try to avoid mistakes in mainstream languages which have
-been already fixed in SML. Let me give a concrete example: in the past I have been
-using Zope and Twisted, which are enterprise-level Python frameworks. They
-share a common implementation of interfaces. I have always had a bad gut
-feeling about Zope interfaces: I always thought they were overkill, complex, and
-not doing by default what they (In my opinion) should do, i.e. interface checking.
-I always thought there should be a better solution (Python 3.0 actually will
-have a simpler implementation of interfaces in the core language)
-but until I studied SML I had no idea of how the solution should look like.
-I think SML has the better module system I have ever seen; and my goal
-here is to popularize it, trying to get some of SML ideas into Python frameworks.
-
-Micro-introduction to functional languages
+-------------------------------
+
+The title of this series of articles should make clear that the series
+is not intended for everybody, it is for dynamic programmers
+only. However, I have yet to specify what do I mean by dynamic
+programmer. For me a dynamic programmer is a non-believer, an heretic
+always ready to challenge the programming orthodoxia; moreover, it is
+a programmer with a Casanova complex, a person loving many languages
+and not marrying any one. A dynamic programmer is the kind of person
+who, when everbody says "the Earth is flat", will wonder: "is the
+Earth *really* flat?". When everybody says that object orientation is
+good and that inheritance is the best thing after sliced bread, a
+dynamic programmer will be suspicious of these claims and will seek
+for alternatives. Exactly in the same way he will escape from the
+enslaving by static typing in C-like languages, he will escape from
+the enslaving by dynamic typing languages, if he finds valid
+alternatives. ll never be loyal to a single language. The *forma
+mentis* of the dynamic programmer is very different from the *forma
+mentis* of the language evangelist: the dynamic programmer can be
+enhaumored of language for a while, but his love will not be
+exclusive.
+This premise is important in order to undestand the goals of this series
+of articles. The goal is not advocating functional programming. Others
+have taken this role. For instance, there is an infamous
+article written by John Hughes in 1984 and titled
+`Why Functional Programming Matters`_ which begins with the sentence
+*This paper is an attempt to demonstrate to the “real world” that functional
+programming is vitally important, and also to help functional programmers
+exploit its advantages to the full by making it clear what those advantages are.*
+This is *not* the goal of this series of articles. The goal is this series if
+merely educational, to broaden the view of the world of my readers,
+including myself (after all, I am the first of my readers ;)
+
+A pragmatic programmer may rightly wonder why to study functional
+languages, since their relevance in enterprise programming is
+negligible, they are practically unknown for system administration
+task, and unless you are a computer science researcher you have no
+need to know them. All that is true; on the other hand, if you
+believed it, you would not be a dynamic programmer, you would have
+stick with C and you would not be a reader of my papers. In
+particular, a dynamic programmer will judge academic languages worth
+of investigation: he may decided not to use them, but still he may get
+some good ideas from them. After all, Python stole the list
+comprehension idea from Haskell and iterators from Icon; Ruby stole
+many ideas from Scheme and Smalltalk; Perl stole from everybody.
+
+.. _Why Functional Programming Matters: http://www.math.chalmers.se/~rjmh/Papers/whyfp.html
+
+Mutation, rebinding and side effects in imperative languages
+------------------------------------------------------------------
+
+Usually functional programming is introduced by discussing
+the drawbacks of imperative programming, in particular the drawbacks
+of mutation and side-effects. I will follow this tradition too
+(a dynamic programmer has no obbligation
+to be innovative at any cost, he may well decide to follow the traditional way).
+
+I will just add that I don't find the arguments against mutation and side effects
+terribly compelling. Certainy there are bugs due to mutation and side
+effects, but in my working experience they are relatively rare. Most of the bugs I have
+in my code are actually issue of business logic (I understood the specs incorrectly or the
+specs where incomplete); many others are due to an incomplete or wrong knowledge of
+the system I have work with; and others are due to the deadlines.
+I would have the same bugs even using a functional language.
+On the other hand, I have nothing against diminuishing the language-related bugs,
+even if they are a minority, so, let me contrast the functional way with the imperative
+way here. I will mostly use Python as an example of imperative language,
+because it is a language that I know well enough, and because it is
+so readable that the examples will be easy to understand even for
+programmers not familiar with Python.
+
+Let me begin by discussing the concept of mutation.
+When you write code like
+
+>>> lst = []; lst.append(1)
+
+in Python you are mutating the list ``lst``: originally it is empty, after the ``append``
+operation it contains the number ``1``. Mutation is a potential source of bugs and
+there are various workaround to make the bugs less likely. For instance, Scheme and
+Ruby use the *bang convention*: the name of a function/method performing mutation ends
+with an exclamation mark, so that they are more visible to the programmer. Python has
+no bang, but there is the convention that mutating methods returns ``None`` and
+not the mutated object. Pure functional languages such as Haskell take a radication
+position and they just make everything immutable. Making everything immutable
+has big advantages especially in multithreaded programming, since different threads cannot
+interfere each other and you can just forget about locks. On the other hand, various
+programming patterns based on mutability (for instance keeping a registry of callbacks)
+becames impossible.
+
+Another very common operation in imperative languages is rebinding a variable.
+When you write code like
+
+>>> i = 1; i = i+1
+
+in Python, you are explicitely rebinding the name ``i`` which originally
+points to the value ``1``, making it to point to the value ``2``.
+This looks like an harmless operation. It is however not so harmless and it may be
+the source of subtle bugs. Consider for instance the following example
+
+>>> f1, f2, f3 = [lambda : i for i in (1, 2, 3)]
+
+Here I am definiting three function without argument (thunks); at each iteration
+in the ``for`` loop the index ``i`` is rebound; at the end of the loop, ``i``
+points to the value ``3``, so all thunks return ``3``:
+
+>>> f1()
+3
+>>> f2()
+3
+>>> f3()
+3
+
+In Haskell, where there is no rebinding, the corresponding list comprehension would
+return three thunks, the first one returning ``1``, the second ``2``, the third ``3`` [#]_.
+
+.. [#] To get the same in Python you can recur to the default argument hack, i.e. you can
+ rewrite ``f1, f2, f3 = [lambda i=i: i for i in (1, 2, 3)]``.
+
+Finally, let me discuss the side effects issue. To be concrete, consider
+the following function with side effects::
+
+ def square(x):
+ print "Called function square with argument %s" % x
+ result = x * x
+ return result
+
+Each time the function ``square`` is called, you get a log message as side effect.
+Unfortunaly, side effects do not play well with memoization techniques and if you cache
+the result of ``square`` with a suitable decorator::
+
+ square = memoize(square)
+
+the log message will not be printed the second time you call the function.
+Generally speaking, programs relying on side effects cannot perform some kind of
+optimization that functional programs cannot perform.
+
+Functional programs have also various formal properties which are appealing for
+language theorists. I will say something more on that on the following articles.
+
+The world of functional languages
------------------------------------------------------------
-Simply put, functional languages are programming languages that try
-very hard to avoid mutation and side effects. There are no pure
-functional languages in use, since an absolutely pure language would
-have to rule out input and output and it would be pretty much useless;
-nevertheless, some functional languages are purer that others. For
-instance, there are languages which have some support for functional
+Often functional languages are classified according to their "purity", i.e. according to
+how hard they try to avoid mutation and side effects. That means that f
+unctional languages are not all equal: some are purer that others.
+There are languages which have some support for functional
programming, but are not considered functional (Python being an
-example); others, like Lisp, which is regarded as the grandfather of
+example); others, like Lisp, the grandfather of
functional languages, that are pretty much regarded as impure
nowadays; Scheme is somewhat perceived as more functional than Common
Lisp, but this is debatable, and in any case it is not very pure;
@@ -86,189 +171,52 @@ be used in an imperative way. This classification is sloppy and wrong
in more than one way; nevertheless it is useful (in my personal
opinion) in the sense that it gives and indication of how much the
language departs from a mainstream imperative language, and how
-difficult it is to learn it. Functional languages come in two kinds:
-dynamically typed (Lisp, Scheme, Erlang ...) and statically typed
-(SML, OCaml, Haskell ...). Programmers used to dynamic typing (i.e
-Pythonistas, Rubystas, etc) will have less trouble to understand
-dynamically typed functional language, so it is more interesting (in
-my opinion) to look at statically typed languages. After all, I think
+difficult it is to learn it.
+Notice that there are no pure
+functional languages in use, since an absolutely pure language would
+have to rule out input and output and it would be pretty much useless.
+
+As always, one must be careful when distinguing the real benefits of
+purity (i.e. fewer bugs, simpler concurrency, better optimizations)
+versus religious thinking (remember OOP bigots?). There are always
+tradeoffs and by no means you should believe *the purest the better*.
+The important things in a language are things like simplicity of use,
+readability, interactivity, debuggability and of course the existence
+of good implementations and good libraries. Still, it is nice to have
+a clean language, if possible.
+
+The world of functional languages is split in two: on one side there
+are the dynamically typed functional languages (Lisp, Scheme, Erlang
+...) on the other side there are the statically typed functional
+languages (SML, OCaml, Haskell ...) . To decide if the dynamically
+type languages are better than the statically typed ones or viceversa
+is an hot debate. For sure, dynamically typed languages are more used
+and better known. Programmers used to dynamic typing (i.e Pythonistas,
+Rubystas, etc) will have less trouble to understand dynamically typed
+functional language than statically types ones.
+
+Still I think it is interesting to look at statically typed languages. After all, I think
a programmer should have at least a statically typed language in his
-toolbox (C++ and Java do not count). SML is the simplest statically
+toolbox (C++ and Java do not count ;) Also, I should notice that there are
+often issues of terminology when talking about types. Most people coming
+from the dynamic world will say that their language
+is strongly typed, contrasting it with weakly typed languages such as C
+or Assembly, where characters are the same as short integers, or with
+Perl, where 2+"3" is a valid expression and returns 5. On the other hand,
+people from the static world will say than dynamically typed languages
+are untyped, since you can change the type of a variable. For instance,
+the following is valid Scheme code::
+
+ (define a 1)
+ (set! a "hello")
+
+In a statically typed language, if a variable is of type integer, it
+stays of type integer, it cannot magically turn into a string.
+
+SML is the simplest statically
language out there, so l decided to start with it.
-
-In the following, I will often contrast the functional way with the imperative
-way, and I will mostly use Python as an example of imperative language,
-because it is a language that I know decently well, and because it is
-so readable that the examples will be easy to understand even for
-programmers not familiar with Python.
-
-Getting an SML compiler
---------------------------------------------------------
-
-SML is a language with many implementations, so if you want to try it,
-you must decide which implementation to use. I have played with some
-of them, including SML of New Jersey (http://www.smlnj.org) and MLton
-(http://mlton.org) but at the end I have decided to use Alice ML,
-since it has a lot of innovative features, an interesting set of libraries, and
-some clean syntax extensions. It is also a very dynamic version of SML,
-so that dynamic programmers will not feel too constrained ;-)
-On Ubuntu, you can install it with
-``apt-get install alice-runtime`` (having added
-``deb http://www.ps.uni-sb.de/alice/download/debian stable contrib`` to your
-``/etc/apt/sources.list``) where on the Mac, you can download the
-``.dmg`` installer and add ``/Applications/Alice.app/Contents/Resources/bin``
-to your PATH. There is also an installer for Windows, but if you are a dynamic
-programmer I do not expect you to use Windows, unless you are forced
-to ;)
-
-Having installed Alice you can start the "interpreter" or REPL
-(read-eval-print-loop) [#]_ as follows::
-
- $ alice
- Alice 1.4 ("Kraftwerk 'Equaliser' Album") mastered 2007/04/24
- ### loaded signature from x-alice:/lib/system/Print
- ### loaded signature from x-alice:/lib/tools/Inspector
- ### loaded signature from x-alice:/lib/distribution/Remote
-
-Now you can write your first mandatory "Hello world" program::
-
- - print "Hello world\n";
- Hello world
- val it : unit = ()
-
-and you are ready to start. If you are an old timer Emacs user, you
-may also want to install (Aqua)Emacs and the sml-mode for Emacs (Alice
-has a custom version of it, that you can download from here_ ).
-
-.. _here: http://www.ps.uni-sb.de/alice/download/sml-mode-4.0+alice.zip
-
-.. [#] In principle, one may argue that choosing a language because of
- its syntax is stupid and that one should look at its features,
- libraries, reliability, etc; in practice, many languages are
- more or less equivalent in terms of features, libraries and
- reliability, so that the main factor for the choice of the
- language is it syntax. Also, when you are a beginner and you
- need to write only simple programs, syntax is all that
- matters.
-
-.. [#] Technically the Alice REPL is not an interpreter: the
- expressions you enter are compiled on the fly and executed
- immediately, not interpreted. However, the overall
- user-experience is the same as if you had an
- interpreter. This approach is commons in ML and Lisp implementations.
-
-It may be useful sometime to contrast Alice with more traditional ML
-implementations (especially to see if the error messages are more
-understandable with another compiler) so you want to run
-``apt-get install smlnj mlton`` as well.
-
-Hello world
----------------------------------------
-
-In the previous section I have shown how the traditional "Hello world"
-program looks in ML. ML is a language without statements (as usual in
-functional languages; statements are actually a bad idea IMO, even
-for imperative languages [#]_) so *print* is a function taking a string in
-input and returning an empty tuple in output, as you
-can see from the last line of output
-
-``val it: unit = ()``
-
-The empty tuple is of type ``unit`` and it is the sole representative of that
-type (i.e. it is a singleton). In general, any object has a type;
-for instance, if you enter the number 42 at the prompt you will get
-
-::
-
- - 42;
- val it: int = 42
-
-The REPL just prints a string representation of the value
-you entered, together with its type (in this case, int).
-
-A point to notice in the "Hello world" program
-is the lack of parenthesis: we wrote ``print
-"Hello world\n";`` and not ``print("Hello world\n")``. This is
-possible since the ``print`` function in ML takes only a *single*
-argument; actually *all* functions in SML takes a single argument!
-
-You may wonder how is it possible to program anything serious if all
-functions take a single argument: the answer is that the single
-argument can be of a composite type, for instance a tuple. So you may
-define functions like the following
-
-::
-
- - fun print2 (arg1, arg2)= (print arg1; print arg2);
- val print2 : string * string -> unit = _fn
-
-Here ``print2`` takes a single argument, which is a 2-tuple (a pair),
-and returns the null (unit) value. The interesting thing is that the
-compiler has been able to figure out by itself that the arguments must
-be of string type, since ``print`` expects a string, and that
-``print2`` will return an ``unit`` value, since ``print`` does so: we
-are seeing here the type -inferencer at work. We also see that a
-function is just a value as any other value, i.e. functions are first
-class objects, as usual in dynamics languages; its type is
-``string * string -> unit``.
-
-Let us check that ``print2`` works as expected::
-
- - print2 ("hello ", "world");
- hello world
- val it: unit = ()
-
-If you want to print a generic number of arguments, you can use a list::
-
- - fun printList lst = app print lst;
- val printList : string list -> unit =_fn
- - printList["Hello ", "World", "!", "\n"];
- Hello World!
- val it : unit = ()
-
-The builtin function ``app`` apply the ``print`` function to each element of the
-list from left to right.
-
-ML does not have default arguments, so you cannot write the analogous
-of this Python function::
-
- >>> def printUser(user="Michele"):
- ... print "Hello,", user
- >>> printUser()
- Hello, Michele
- >>> printUser("Mario")
- Hello, Mario
-
-In Python, ``printUser`` can be called with zero or one arguments, but in ML this
-is not possible, since functions with different arity are of a different type and
-ML does not support function overloading; however, you can however work around
-this feature [#]_ by using options. ML has a standard *option* type, used for
-objects which may have a NONE value; any object can be converted into an option
-by wrapping it with SOME. An example will make it clear. Let me define::
-
- - fun printUser(userOpt: string option) =
- printList ["Hello, ", getOpt(userOpt, "Michele"), "\n"];
- val printUser : string option -> unit = _fn
-
-where *getOpt(opt, default)* is a builtin function which returns the
-value of the option, or the default if the option have value NONE. We
-may call *printUser* as follows::
-
- - printUser NONE;
- Hello, Michele
- - printUser (SOME "Mario");
- Hello, Mario
-
-The syntax is definitively ugly, and there are actually clever tricks
-to implement default arguments in a nicer way, but they are too
-advanced for this introduction. But stay tuned for the next issues ;)
-
-.. [#] Incidentally, in current Python ``print`` is a statement and it
- does not return anything, but in Python 3000 ``print`` will be
- function returning None
-
-.. [#] The lack of function overloading may be seen as a feature or as
- a limitation, depending on the reader.
+In the next installment I will show you "hello world" in SML as well as
+a few other things, stay tuned!
----
diff --git a/ml/text.txt b/ml/text.txt
index 342513c..97856a6 100644
--- a/ml/text.txt
+++ b/ml/text.txt
@@ -13,310 +13,24 @@ statically typed languages. In order to be concrete, I will consider languages
in the ML family, because they are pretty nice and much easier to understand
that Haskell.
-Academic languages vs enterprise languages
------------------------------------------------------------
+Text processing in SML
+----------------------------------------------
-There is a widespread misconception that academic languages are clean but
-unpractical, whereas enterprise languages are practical but dirty. I never
-bought that argument, since I have seen examples of the opposite, and also
-because I always believed that a language can be both clean *and* practical.
+Scripting languages, starting from Perl, have a very strong tradition in the field
+of text processing. In comparison, SML has very primitive libraries. Still, it is
+not difficult to implement a few basic facilities. In this assessment, as a
+learning exercise, I will show how can implement a simple template mechanism.
+In order to do so, the first thing to discuss is the regular expression.
+More or less all SML implementations have they own regular expression
+library; here I will consider only Alice ML library, which is very simple
+and needs to be supplemented by some utility in order to become
+usable.
-For instance in my opinion both Python and Ruby are reasonably clean and
-reasonably practical and this is part of the reason why they are having so
-much success lately. I believe the reason why they are so clean is that from
-the beginning they managed to stay away from optimization, the root of all evil.
-On the other hand, I am also convinced that it possible to write a language
-which is both practical, clean and fast. Unfortunately, such a language does
-not exist yet. SML is fast and somewhat clean but it is definitely not practical.
-Here by practical I mean enterprise-ready.
+$pippo:
+$hello
+$are pippo lippo
-There is an irriducible aporia_ between enterprise programmers and
-academics: people working on enterprise are facing every day problems
-that are already solved; to them, the importanting thing is to avoid
-reinventing the wheel. Given two languages, one that provides a solution
-to a problem they have, and one providing the tools to build the solution
-themselves, they prefer the first one. This is natural since in an enterprise
-environment one has deadlines and it is essential to be as productive
-as possible; of course the best way to be productive is to *not write* code,
-and to re-use code written by others.
-On the other hand, in academia one has to do with new problems, or
-with new techniques to solve old problems: the proof of concept is
-more important than the practical implementation. Also, academics
-are (rightly) interested in writing papers, not in writing code. For all these
-reasons it is clear that you cannot face an
-academic language such as SML with an enterprise mindset.
-For instance, if you come from an enterprise environment you will be used to
-expect ready availabity of a nearly infinite number
-of libraries doing everything you may need and more. This is certainly
-the situation for all enterprise-level languages such as C, C++, Java, C#,
-Perl, Python and now even Ruby, which is younger but rapidly spreading.
+fun pippo {hello, are} = "" ^ hello ^ "" ^ are " pippo lippo"
-As I said many time, I assume that my readers are dynamic programmers,
-well acquainted with scripting languages; I feel the urge warn them that the
-situation in SML is very different than in Perl, Python or Ruby [#]_ . Here
-are a few differences.
-1.
- Scripting languages have a dominant (sometimes unique)
- implementation; ML has dozens of implementations, each with
- useful features which are not in the standard, so that
- different implementations provides incompatible extensions.
-
-2.
- Scripting languages have a BDFL_ (Benevolent Dictator For Life,
- i.e. Guido van Rossum for Python, Larry Wall for Perl, Yukihiro
- Matsumoto for Ruby) whereas SML is a language designed by
- committee. That means that a scripting language can evolve much
- *much* faster than a language designed by committee. Also,
- languages designed by committee are compromises whereas
- single-person languages are much more consistent.
-
-3.
- SML has a very small community: the support you can have in newsgroup
- and mailing lists is simply not comparable to the support you can get in
- the scripting languages mailing list and newsgroups.
-
- if Andreas Rossberg won the first price
- at the lottery and decided to leave for a tropical paradise, nobody would
- provide support for Alice ML on the mailing list
-
-4.
- SML is a deserted language, even compared to non-mainstream languages
- as OCaml, Haskell, Scheme, Common Lisp, Smalltalk, Tcl, ...; it is perhaps the
- least used language I have ever seen and this is a pity.
-
-5.
- Generally speaking, if you want bindings for external libraries you have to write
- it yourself; there are no standard bindings for GUI toolkits, database drivers,
- scientific programming libraries.
-
-6.
- Scripting languages have traditionally a strong support for Web programming
- which is next to absent in SML.
-
-
-All the issues I have just listed are just accidental, not
-structural: in principle one could just solve them by writing code; in principle
-a DHH_ could take an SML implementation and make it the next Ruby on Rails.
-However that has not happened yet, so if you want to work with SML right now you
-will be forced to reinvent the wheel. In this article I will reinvent a few wheels,
-just to be able to give some meaninful example to the enterprise programmer
-in the next articles.
-
-.. _aporia: http://en.wikipedia.org/wiki/Aporia
-.. _BDFL: http://en.wikipedia.org/wiki/BDFL
-.. _DHH: http://en.wikipedia.org/wiki/DHH
-
-.. [#] Notice that I put Perl, Python and Ruby in the mainstream
- languages, since even if the number of dynamic programmers is
- inferior the number of say C/C++, Java or .NET programmers,
- in practice scripting programmers have available nearly everything the
- other languages have available, and much better languages.
-
-String interpolation the practical way
---------------------------------------------------------
-
-Every language has some builtin mechanism to do string interpolation: C
-has ``sprintf``, Python has the ``%`` operator, Lisp has ``format``, but
-SML has none. Therefore I will provide here a very simple interpolation library
-for SML, so that I will be able to provide more interesting examples later.
-The library is based on list processing.
-Lists are the most common data structures in
-functional languages (they formed the core of Lisp at its beginning)
-and you simply cannot live without them. You should look at the `basis library`_
-to see what you can do with lists. Here I will just say that SML lists are
-linked lists in the same sense of Lisp or Scheme (with the difference
-that they are immutable) and not as in Python (Python lists are actually arrays,
-Python is abusing the name). Just as in Scheme, where
-
- ``(list 1 2)``
-
-is a shortcut for
-
- ``(cons 1 (cons 2 '()))``
-
-in ML
-
- ``[1, 2]``
-
-is a shortcut for
-
- ``1::2::[]``
-
-and ``::`` is the *cons* operator (short for constructor).
-If you don't know Scheme, a SML lists should be thought of as of a
-nested Python list, i.e.
-
- ``[1, 2] (SML) => [1, [2, []]] (Python)``
-
-.. _basis library: http://www.standardml.org/Basis/list.html
-
-Here is the code::
-
- $ cat format.aml
-
-.. include:: format.aml
- :literal:
-
-A few comments are in order.
-
-1.
- We used the ability to define exceptions with a value:
- ``exception ArityError of string`` means that ``ArityError`` accepts
- a string argument (the error message).
-
-2.
- We used the standard library ``String.fields`` utility, which splits a string
- according to a delimiter; in our case ``String.fields (fn c => c = #"$") templ``
- splits the template into a list a strings using the character ``$`` as delimiter.
- In SML characters are specified with a sharp notation, so the character ``$``
- is written ``#"$"``.
-
-3.
- At the core of the algorithm is the recursive function
- ``interp'(templN1, argsN)`` which takes a list of N+1 strings (the
- splitted template) and a list of N strings (the arguments) and returns the
- interpolated string. For instance, for the template ``The square of $ is $\n``
- which contains N=2 delimiters,
- ``templN1`` would be ``["The square of ", " is ", "\n"]``.
-
-4.
- The builtin ``concat`` concatenates a list of strings whereas ``rev`` reverts
- a list; you are advised to read the documentation or any book on SML for more.
-
-String interpolation the mathematical way
--------------------------------------------------
-
-Functional languages have a reputation for abstractness
-but in this series of articles I have focused solely on very concrete
-earth-bound aspects. To correct the balance, in this section I will discuss
-a few programming techniques which are quite common in
-functional languages, but unheard of in "regular" languages, and that
-require a mathematically-inclined mind to be understood. I am doing so
-because these tricks are actually used a lot in functional languages, and
-I don't want to give a false impression by ignoring them
-and just discussing the easy things.
-
-For instance, you should not believe that functional
-programming is just about functions; there is an higher order
-approach to functional programming, in which the primary objects
-are not functions, but operators operating on functions, the so
-called *combinators*. For people with a background in Physics, I will
-say this is exactly analogous to the switch from the
-Schroedinger picture, in which the emphasis is on the wave functions,
-to the Heisenberg picture, in which the emphasis is on the quantum
-operators.
-
-Having warned my readers that this section is not for the faint of heart
-(but you may safely skip it if you don't feel adventurous)
-I will start from the concatenation operator
-
-::
-
- ^ : string * string -> string
-
-which satifies the properties::
-
- a ^ (b ^ c) = (a ^ b) ^ c = a ^ b ^ c
- a ^ "" = "" ^ a = a
-
-Mathematically speaking, the set of strings in SML
-is a monoid_ with respect to concatenation and the empty
-string "" is the identity element.
-
-.. _monoid: http://en.wikipedia.org/wiki/Monoid
-.. _group: http://en.wikipedia.org/wiki/Group_%28mathematics%29
-.. _group representations: http://en.wikipedia.org/wiki/Group_representation
-
-If you have a background in Mathematics or in Physics you will be familiar
-with the theory of `group representations`_; in particular groups (and monoids
-too) can be represented with operators operating on function spaces.
-In the case at hand, I can define a lift transformation converting plain
-strings into operators by preserving the composition law::
-
- - fun L s f s' = f (s' ^ s);
- val L : string -> (string -> 'a) -> string -> 'a = _fn
-
-(this is the same as ``fun L s = fn f => fn s' => f (s' ^ s)``).
-In other words, ``L s`` is an operator (combinator) taking a function and
-returning a function, with the property
-
- ``L (s1 ^ s2) = (L s1) o (L s2)``
-
-where ``o`` denotes the composition of operators. Just as ``L`` is an upgrade
-operation, promoving a plain simple string to the celestial world of operators
-in function spaces, I can define a downgrade operation ``l``, demoving
-celestial operators back to earthly strings::
-
- - fun l O = O Fn. id "";
- val l : (('a -> 'a) -> string -> 'b) -> 'b = _fn
-
-``l`` takes the operator, applies it to the identity function and returns a function
-which is then applied to the empty string, finally bringing back at home a
-plain string; ``l`` is the inverse of ``L``, i.e. ``l o L`` is the identity
-in the space of strings where ``L o l`` is the identity in the space
-of operators. You can try yourself at the prompt that
-
-::
-
- - l(L"a" o L"b");
- val it : string = "ab"
-
-so we succeded in making simple things hard and abstract. But not happy with
-that, we are going to make things harder and more abstract, by defining
-another combinator taking a function and returning an higher order function::
-
- - fun str f s s' = f (s ^ s')
- val str : (string -> 'a) -> string -> string -> 'a = _fn
-
-(this is the same as ``fun str f = fn s => fn s' => f ( s ^ s')``).
-This combinator is so abstract than even when lift back to the mortal world
-it is more than a mere string, it is actually the identity function on strings::
-
- - l str;
- val it : string -> string = _fn
- - (l str) "hello";
- val it : string = "hello"
-
-The ``str`` combinator can be composed in the celestial word: when lift
-back to the mortal world, it returns a higher order version of the concatenation
-operator::
-
- - l (str o str);
- val it : string -> string -> string = _fn
-
- - l (str o str) "hello" " world";
- val it : string = "hello world"
-
-We can also compose ``str`` with other combinators and we can write things like::
-
- - l( L"a" o str o L"b" o str o L"c") "?" ":";
- val it : string = "a?b:c"
-
-In other words, we have reinvented string interpolation the difficult way.
-Still, there is at least an advantage of this approach with respect to the
-approach we used in the previous paragraph: combinator-based string interpolation
-happens at compile type and *mistakes are discovered by the compiler*, not
-at runtime. Moreover, the approach can be easily extended to manage
-different types: for instance, if we have to do with numbers, we
-can define the combinators::
-
- - fun int f s n = f (s ^ (Int.toString n))
- val str : (string -> 'a) -> string -> string -> 'a = _fn
-
- - fun real f s n = f (s ^ (Real.toString n))
- val real : (string -> 'a) -> string -> real -> 'a = _fn
-
-and use them as follows::
-
- - print (l(int o L" / " o int o L" = " o real o L"\n") 5 2 2.5);
- 5 / 2 = 2.5
- val it : unit = ()
-
-If you make a mistake like using an int instead of a real, or if you forget an
-argument, you will get a compile time type error.
-
-----
-
-*Give a man a fish and he will eat for a day. Teach a man to fish and he will eat
-for the rest of his life.* -- Chinese proverb
+link:<a href="$u">$a</a>
diff --git a/ml/types.txt b/ml/types.txt
index e07798b..f7f54f2 100644
--- a/ml/types.txt
+++ b/ml/types.txt
@@ -22,7 +22,9 @@ Smalltalk. In such languages you here often the mantra *everything is
an object* meaning that everything is a first class value which can
created and inspected at runtime. Moreover, any object has a class,
and classes themselves are objects, i.e. they are instances of
-metaclasses. SML has a type system which is completely different from
+metaclasses. The first step in order to learn the SML type system is to forget
+everything you know about object systems.
+SML has a type system which is completely different from
the object system of dynamic language. There a lots of things which
are not first class values (structures, exceptions, signatures, ...)
and even types are such: in SML any value has a type, but types itself
@@ -34,9 +36,8 @@ gives an error::
Just as structures types live in a separated namespace.
SML types are not classes in any OO sense of the term,
-you cannot introspect them, there are no methods and no inheritance,
-thus the first step in order to learn the SML type system is to forget
-everything you know about OOP. Having said that, you can certainly
+you cannot introspect them, there are no methods and no inheritance.
+Having said that, you can certainly
program in an OO style in ML, but you don't use types for OOP, types are used
internally by the compiler, but from the user's point of view they are just
labels attached to values.
@@ -53,10 +54,11 @@ as follows::
- datatype int_or_string = INT of int | STR of string;
datatype int_or_string = INT of int | STR of string
-This definitions tells the compiler that the label ``int_or_string`` has to
-be used when showing the signature of objects of the newly defined types;
-but the important things are the constructors, which are able to convert (or *cast*)
-primitive values to values of the new type::
+This definitions tells the compiler that the label ``int_or_string``
+has to be used when showing the signature of objects of the newly
+defined types; but the important things are the constructors, which
+are able to convert (or *cast*) primitive values to values of the new
+type::
- val v1 = INT 1;
val v1 : int_or_string = INT 1
@@ -111,10 +113,97 @@ but we can define::
- [INT 1, STR "two"];
val it : int_or_string list = [INT 1, STR "two"]
-As Harper puts it, *heterogenity is a special case of homogenity*.
+As Harper puts it, *heterogenity is a special case of homogenity* [#]_.
-It is also possible to define parametric types, where the constructor(s) can
-accept any type as argument. For instance, we can define a parametric box type::
+.. [#] "Programming in Standard ML", section 10.4
+
+Composite types and recursive types
+--------------------------------------------------
+
+We may define composite types, i.e. types with
+*syntactic arity* greater than one, like the following::
+
+ - datatype string_int = STRING_INT of string * int;
+ datatype string_int = STRING_INT of string * int
+
+The uppercase identifier is called the constructors of the datatype, and can
+be used to make concrete instances of the type::
+
+ - STRING_INT("hello", 1);
+ val it : string_int = STRING_INT ("hello", 1)
+
+where the constructor is a first class value, being simply a function taking
+a pair ``(string, int)`` as input::
+
+ - STRING_INT;
+ val it : string * int -> string_int = _fn
+
+Moreover, SML allows to define recursive types. For instance, a file system is a typical
+example of recursion, since directories can contains directories at any
+level of nesting. It can modeled with the following recursive datatype::
+
+ - datatype file_or_dir = File of string | Dir of string * file_or_dir list
+ datatype file_or_dir = Dir of string * file_or_dir list | File of string
+
+Here a file is identified by its pathname, whereas a directory is identified by
+its pathname and its contents, a list of files or directories. Here are a few
+example of usages::
+
+ - val f1 = File "file1";
+ val f1 : file_or_dir = File "file1"
+ -val f2 = File "file2";
+ val f2 : file_or_dir = File "file2"
+ - val d = Dir("dir", [f1, f2]);
+ val d : file_or_dir = Dir ("dir", [File "file1", File "file2"])
+ - val p = Dir("parent", [d, File "file3"]);
+ val p : file_or_dir =
+ Dir ("parent", [Dir ("dir", [File "file1", File "file2"]), File "file3"])
+
+Recursive datatypes are naturally associated with recursive functions;
+for instance you can pretty print the content of a directory with the
+following function::
+
+ val prettyPrint = let
+ val rec prettyPrint' =
+ fn (File name, spaces) => print (spaces ^ name ^ "\n")
+ | (Dir (name, contents), spaces) => (
+ print (spaces ^ name ^ "\n");
+ app (fn c => prettyPrint'(c, spaces ^ " ")) contents)
+ in
+ fn f_or_d => prettyPrint' (f_or_d, "")
+ end
+
+ - do prettyPrint p
+ parent
+ dir
+ file1
+ file2
+ file3
+
+SML also allows to define types with syntactic arity zero, i.e.
+enumeration types, like the following one::
+
+ - datatype color = RED|GREEN|BLUE;
+ datatype color = BLUE | GREEN | RED
+
+For enumeration types the name "constructor" is rather improper, since constructor are
+just values::
+
+ - RED;
+ val it : color = RED
+
+ - GREEN;
+ val it : color = GREEN
+
+ - BLUE;
+ val it : color = BLUE
+
+Parametric types
+---------------------------------------
+
+By far, the most interesting types in SML are the parametric types,
+where the constructor(s) can accept any type as argument. The simplest
+parametric type is the box type::
- datatype 'a box = Box of 'a;
datatype 'a box = Box of 'a
@@ -139,32 +228,12 @@ Notice that it is pretty easy to define a function extracting the inner value fr
- fun unbox (Box x) = x;
-The builtin ``Ref`` type works as a box and the builtin``!`` function works
+The builtin ``ref`` type works as a box and the builtin``!`` function works
as ``unbox``, the different is that our ``Box`` is immutable, i.e. we do not
have an equivalent of the assigment function ``:=``.
-More on datatypes
------------------------------------------------------------
-
-We may define composite types, i.e. types with
-*syntactic arity* greater than one, like the following::
-
- - datatype string_int = STRING_INT of string * int;
- datatype string_int = STRING_INT of string * int
-
-The uppercase identifier is called the constructors of the datatype, and can
-be used to make concrete instances of the type::
-
- - STRING_INT("hello", 1);
- val it : string_int = STRING_INT ("hello", 1)
-
-where the constructor is a first class value, being simply a function taking
-a pair ``(string, int)`` as input::
-
- - STRING_INT;
- val it : string * int -> string_int = _fn
-
-A more interesting example could be a *named_function* type corresponding
+A more interesting example of parametric type
+could be a *named_function* type corresponding
to a pair *(function, name)*, where *function* can be of any functions, whereas
*name* is a string.
We can define it as follows::
@@ -187,23 +256,6 @@ Here is an example::
(* giving a name to the function x=>2*x *)
val it = NAMED_FUNCTION (fn,"double") : (int,int) named_function
-SML also allows to define enumeration types, like the following one::
-
- - datatype color = RED|GREEN|BLUE;
- datatype color = BLUE | GREEN | RED
-
-but for enumeration types the name is rather improper, since they are
-just values::
-
- - RED;
- val it : color = RED
-
- - GREEN;
- val it : color = GREEN
-
- - BLUE;
- val it : color = BLUE
-
Finally, SML let you define aliases for previously defined types,
or builtin types, by using the ``type`` keyword::
@@ -215,9 +267,9 @@ or builtin types, by using the ``type`` keyword::
- type ls = List.list;
type ls = List.list
-This is especially convenient when writing signatures.
+This is especially convenient when writing (abstract) signatures.
-Poor man's polymorphism
+Runtime polymorphism
-----------------------------------------------------------------
Possibly the most used word in statically typed languages is *polymorphism*.