summaryrefslogtreecommitdiff
path: root/ml
diff options
context:
space:
mode:
authormichele.simionato <devnull@localhost>2007-12-09 15:58:39 +0000
committermichele.simionato <devnull@localhost>2007-12-09 15:58:39 +0000
commit9b1b7aa5a6b762b1110391c79241a1b8dccbfa83 (patch)
tree42dd0820d6699d9ffb47519269c80f806c563633 /ml
parentc16096239a55e74292a32fe16c834797f6854edd (diff)
downloadmicheles-9b1b7aa5a6b762b1110391c79241a1b8dccbfa83.tar.gz
Various enhancements
Diffstat (limited to 'ml')
-rw-r--r--ml/format.aml40
-rw-r--r--ml/intro.txt50
-rw-r--r--ml/loops.txt2
-rw-r--r--ml/modules.txt8
-rw-r--r--ml/objects.txt119
-rw-r--r--ml/text.txt319
-rw-r--r--ml/types.txt163
7 files changed, 423 insertions, 278 deletions
diff --git a/ml/format.aml b/ml/format.aml
new file mode 100644
index 0000000..1c6427a
--- /dev/null
+++ b/ml/format.aml
@@ -0,0 +1,40 @@
+(*
+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
+
+do print(format "The square of $ is $\n" ["3", "9"])
+*)
+
+signature FORMAT = sig
+ val format : string -> string list -> string
+end
+
+structure Format = struct
+ exception ArityError of string
+
+ fun checkArity(templN1, argsN) = let
+ val n1 = Int.toString (length templN1 - 1)
+ val n = Int.toString (length argsN)
+ in
+ if n1=n then () else raise ArityError("Expected "^n1^" arguments, got"^n)
+ end
+
+ val rec interp' =
+ fn (t :: [], [], acc) => concat(rev (t :: acc))
+ | (t :: templ, a :: args, acc) => interp'(templ, args, t :: a :: acc)
+ | _ => raise ArityError "This should never happen"
+
+ and interp'' =
+ fn (templN1, argsN) => (
+ checkArity (templN1, argsN); interp' (templN1, argsN, []))
+
+ and format =
+ fn templ => let
+ val templN1 = String.fields (fn c => c = #"$") templ
+ in
+ fn args => interp'' (templN1, args)
+ end
+
+end: FORMAT
diff --git a/ml/intro.txt b/ml/intro.txt
index 0bcdc61..803bdf6 100644
--- a/ml/intro.txt
+++ b/ml/intro.txt
@@ -68,33 +68,33 @@ here is to popularize it, trying to get some of SML ideas into Python frameworks
Micro-introduction to 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;
+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
-programming, but are not considered functional (for instance, Python); others,
-like Lisp, which is regarded as 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; Standard ML (SML) is
-often perceived as purer than Scheme, but mostly because of
-syntactical convenience, not of technical merit; Haskell is possibly
-the purest functional language commonly used, but still can 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 a programmer should have at
-least a statically typed language in his toolbox (C++ and Java do not
-count). SML is the simplest statically language out there, so l decided to
-start with it.
+programming, but are not considered functional (Python being an
+example); others, like Lisp, which is regarded as 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;
+Standard ML (SML) is often perceived as purer than Scheme, but mostly
+because of syntactical convenience, not of technical merit; Haskell is
+possibly the purest functional language commonly used, but still can
+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
+a programmer should have at least a statically typed language in his
+toolbox (C++ and Java do not count). 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,
diff --git a/ml/loops.txt b/ml/loops.txt
index a058551..a3c2144 100644
--- a/ml/loops.txt
+++ b/ml/loops.txt
@@ -113,7 +113,7 @@ functions. You, as a dynamic programmer, will probably already know
both concepts, but in languages such are Python, Perl, or even Common Lisp
they are not as pervasive as in truly functional languages.
-Recursion
+Loops and recursion
----------------------------------------------------
Perhaps the most common construct in imperative programming is the *for* loop;
diff --git a/ml/modules.txt b/ml/modules.txt
index 43a5664..0775bbe 100644
--- a/ml/modules.txt
+++ b/ml/modules.txt
@@ -81,7 +81,7 @@ converting it into a function ``iterator -> unit``, whereas
iterator. For instance, suppose we want to define an utility to
convert to upper case a text file, by applying the function [#]_
- ``- fun upper str = String.implode (map Char.toUpper (String.explode str));``
+ ``- fun upper str = String.map Char.toUpper str;``
to each line; we can test it by writing a file like the following::
@@ -276,7 +276,7 @@ defines the type ``list``, whereas the ``Vector`` structure defines
the type ``vector``; both types have a common alias ``t``.
``list`` and ``vector`` are container types, in the sense that a
list/vector may contain objects of any type, as long as all the elements
-are homegenous, so you may have a ``string list``, an ``int list``,
+are homogenous, so you may have a ``string list``, an ``int list``,
a ``string list list``, etc [#]_::
- val lst = [0,1,2];
@@ -371,7 +371,8 @@ interfaces. Code written to work with a ``SIMPLE_SEQUENCE`` interface
will automatically work with lists, vectors, and any data structure
(builtin *and* custom) satisfying the interface.
-.. [#] Notice that since SML is a functional language, both lists and vectors are immutable.
+.. [#] Notice that since SML is a functional language, both lists and
+ vectors are immutable.
.. _information hiding: http://en.wikipedia.org/wiki/Information_hiding
.. _facade pattern: http://en.wikipedia.org/wiki/Facade_pattern
@@ -381,3 +382,4 @@ will automatically work with lists, vectors, and any data structure
*divide et impera*
-- old Roman saying
+
diff --git a/ml/objects.txt b/ml/objects.txt
index 9458cea..4cb4a13 100644
--- a/ml/objects.txt
+++ b/ml/objects.txt
@@ -1,10 +1,10 @@
-Functional Programming For Dynamic Programmers - Part 5
+Functional Programming For Dynamic Programmers - Part 6
=======================================================================
:author: Michele Simionato
:date: December 2007
-This is the fifth of a series of articles on functional programming in
+This is the sixth 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
@@ -59,67 +59,6 @@ are functional, i.e. immutable: there is no way to change the value of a field,
you must create an entirely new record with a different value if you want to
simulate a record update.
-Lists
----------------------------------------------------
-
-Lists are the most common data structures in functional languages (they formed
-the core of Lisp at its beginning) and there are many facilities to manage them and
-to iterate over them. For instance, in the first article of this series, I showed the
-``app`` builtin, to apply a function over the elements of a list; there is also
-a ``map`` builtin to build a new list from an old one::
-
- - val doubles = map (fn x => 2*x) [1,2,3];
- val doubles : int list = [2, 4, 6]
-
-and a ``filter`` function to extract the sublist of elements satisfying a given predicate::
-
- - fun isEven n = n mod 2 = 0;
- - val even = List.filter isEven [1,2,3];
- val even : int list = [2]
-
-Moreover, you can append lists with the ``@`` operator::
-
- - [1] @ [2] @ [3,4];
- val it : int list = [1, 2, 3, 4]
-
-There are other standard facilities and you can look at the documentation
-http://www.standardml.org/Basis/list.html to find them all.
-
-ML lists are linked lists in the same sense of Lisp or Scheme [#]_, however they
-are immutable. 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).
-
-
-.. [#] Python lists are actually arrays, not lists
-
-.. [#] If you don't know Scheme,
-
- [1, 2] (ML) => [1,[2,[]]] (Python)
-
-
-
-It is also possible to apply a binary operator to a list, via the ``foldl`` and ``foldr``
-functions::
-
- - val sum = foldl op+ 0 [1,2,3];
- val sum : int = 6
-
-
fun enum n = lazy n :: enum (n+1)
(for instance, a log file); how can we process it? The simplest possibility is
@@ -223,60 +162,6 @@ end
val length : 'a aggr -> int
val sub : 'a aggr * int -> int
end
- -
-
-ok
-
-"Hello World", part II
--------------------------------------------------
-
-On the other hand, print2 cannot accept anything different than strings::
-
- - print2("the answer is ", 42);
- 1.0-1.28: mismatch on application: expression type
- string * int
- does not match function's argument type
- string * string
- because type
- int
- does not unify with
- string
-
-To print a string and a number, we should define another function::
-
- - fun print_string_int(s, i)=(print s; print (Int.toString(i)));
- val print_string_int : string * int -> unit = _fn
- - print_string_int("The answer is ", 42);
- The answer is 42val it : unit = ()
-
-This is clearly unpractical, since we can't define an infinite number
-of ``print`` functions to be able to print all the potentially
-infinite types we can have in our programs. Fortunately there are
-utility library for converting objects in strings (even if not
-standard :-(). SML/NJ has the library FormatComb, to be used as
-follows (from now on, for concision sake, I will omit to write down
-the full output of the REPL)::
-
- - open FormatComb;
- - print (format (text "The value is " o int o text ".") 42);
- The value is 42
-
-The syntax looks strange (especially the "o" operator) and if you
-forget the parenthesis you will get funny error messages. To
-understand what is going on, you will need to master SML to a much
-more advanced level than this first paper is meant to, so you will
-have to wait for "Hello World, Part II" ;) Here I will content myself
-to notice that the format utility requires you to specify the types of
-the arguments. SML is a *typeful* language: the compiler must know the
-types of all your variables *before running the program*. In a
-dynamic language, instead, the types can be introspected at runtime
-and there is no need to specify them. This is most of the times an
-advantage, however, it also leads to type errors that cannot occur in
-a statically typed language such as SML (also called *type safe*
-languages). Moreover, SML compilers may perform optimization which are
-impossible in a dynamic language (but this is of lesser relevance,
-since any user of dynamic languages will use C libraries if speed is
-an issue).
Higher order functions can also be used to implement poor man's object
diff --git a/ml/text.txt b/ml/text.txt
index c53cc55..63a0874 100644
--- a/ml/text.txt
+++ b/ml/text.txt
@@ -1,5 +1,322 @@
-Text processing in SML
+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 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
diff --git a/ml/types.txt b/ml/types.txt
index 98c65fc..e07798b 100644
--- a/ml/types.txt
+++ b/ml/types.txt
@@ -1,10 +1,10 @@
-Functional Programming For Dynamic Programmers - Part 4
+Functional Programming For Dynamic Programmers - Part 5
=======================================================
:author: Michele Simionato
:date: December 2007
-This is the fourth of a series of articles on functional programming in
+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
@@ -260,154 +260,55 @@ This approach to polymorphism works for simple things, but it is not practical,
nor extensible: this is the reason why SML provides an industrial strenght
mechanism to support polymorphism, *functors*.
-Functors
-------------------------------------------------------
+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.
-As we saw in the previous article, structures are not first class objects and
+
+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, such as the following::
-
- - functor Sequence(s:SIMPLE_SEQUENCE) = funct
- type t = s.t
- length =
- end;
-
-
-Input and Output revisited
---------------------------------------------------------------
-
-Another typical use case the, which is used when you
-want to provide a simple interface to complex library. For instance,
-consider two structures of the standard library, ``TextIO`` and ``BinIO``;
-their principal signatures are different since they have different implementations,
-but still they share many features and it is possibile to define subsignature(s)
-matching both.
-In particular, it is possible to define a simplified interface (a *facade*) which is
-common to both as follows::
-
- - signature SIMPLE_IO = sig
- type instream
- type outstream
- type vector
- val openIn: string-> instream
- val closeIn: instream -> unit
- val openOut: string-> outstream
- val closeOut: outstream -> unit
- val inputAll: instream -> vector
- val output: outstream * vector -> unit
- end;
+declaration ``functor Name(Struct:SIGNATURE) = funct ... end``.
-To an application compatible with the SIMPLE_IO signature, both ``TextIO``
-and ``BinIO`` will look the same: giving an explicit signature
-enhances genericity, modularity and code reuse. This is expecially true
-if you use *opaque importing*, i.e. the ``:>`` syntax exemplified below::
-
- - structure T=TextIO:>SIMPLE_IO;
- structure T : SIMPLE_IO
-
- - structure B=BinIO:>SIMPLE_IO;
- structure B : SIMPLE_IO
-
- - val f = B.openIn "three-lines.txt";
- val f : B.instream = _val
- - val x = Byte.bytesToString (B.inputAll f);
- 1.8-1.41: mismatch on application: expression type
- B.vector
- does not match function's argument type
- t
- because type
- B.vector
- does not unify with
- Word8Vector.vector
- -
-
-Transparent signatures
----------------------------------
-
-and you could import only a subset of the features provided by as follows::
-
- - structure T = TextIO:SIMPLE_IO;
- structure T :
- sig
- type vector = vector
- type instream = TextIO.instream
- type outstream = TextIO.outstream
- val openIn : string -> TextIO.instream
- val inputAll : TextIO.instream -> vector
- val closeIn : TextIO.instream -> unit
- val openOut : string -> TextIO.outstream
- val output : TextIO.outstream * vector -> unit
- val closeOut : TextIO.outstream -> unit
- end = TextIO
-
-::
-
- structure B = BinIO:SIMPLE_IO;
- structure B :
- sig
- type vector = vector
- type instream = BinIO.instream
- type outstream = BinIO.outstream
- val inputAll : BinIO.instream -> vector
- val closeIn : BinIO.instream -> unit
- val output : BinIO.outstream * vector -> unit
- val closeOut : BinIO.outstream -> unit
- val openIn : string -> BinIO.instream
- val openOut : string -> BinIO.outstream
- end = BinIO
-
- - open BinIO:SIMPLE_IO;
- - val f = B.openIn "three-lines.txt";
- val f : BinIO.instream = _val
- - val x = Byte.bytesToString (B.inputAll f);
- - B.closeIn f;
-
-
-The rationale behind opaque signatures
-is that they make easier to replace an implementation
-with another without breaking the interface.
-
-On the other hand, the application program has still the choice to ignore
-your proposed signature and use the principal signature: in this case of course,
-he will take his risks, since the interface of private methods is not guaranteed
-and could change in future versions of the library, but he can do that if he
-needs it.
-
-
- - functor Wrap(SimpleIO:SIMPLE_IO) = struct
- val inputAll = SimpleIO.inputAll
- output = SimpleIO.output
- fun fileIn manage fname = let
- val file = openIn fname
- in
- manage file finally closeIn file
- end
- fun fileOut manage fname = let
- val file = openOut fname
- in
- manage file finally closeOut file
- end
- 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 = Wrap(TextIO)
- - structure B = Wrap(BinIO)
+ - 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.
+code for the structures ``T`` and ``B`` in the *client* program.
+
+ ``- T.withInputFile "three-lines.txt" (print o T.inputAll)``
+
+----
-``do print (IO.fileIn IO.inputAll "three-lines.txt")``
+ *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