Re: Lisp dinamism vs ML safety
- From: Jon Harrop <jon@xxxxxxxxxxxxxxxxx>
- Date: Fri, 13 Oct 2006 02:32:20 +0100
Wolfram Fenske wrote:
Suppose I need a stack. A generic class seems like a reasonable
choice:
--8<---------------cut here---------------start------------->8---
class ['a] stack =
object (self)
val mutable list = ( [] : 'a list ) (* instance variable *)
method push x = (* push method *)
list <- x :: list
method pop = (* pop method *)
let result = hd list in
list <- tl list;
result
method peek = (* peek method *)
hd list
method size = (* size method *)
length list
end;;
--8<---------------cut here---------------end--------------->8---
No reason to use a generic class when there is already a built-in Stack
module.
Now, for debugging purposes I want to log each element I pop off the
stack. In Python, e. g., I could just say:
--8<---------------cut here---------------start------------->8---
def pop(self):
result = self.list[0]
self.list = list[1:]
print repr(result) # repr returns the representation of its
argument
return result
--8<---------------cut here---------------end--------------->8---
(In Common Lisp, I'd write (prin1 result) where I wrote print
repr(result).)
My OCaml solution is not so easy because I don't know how to write
something like repr or prin1 in OCaml.
Absolutely. You can't write a generic print function in OCaml because it has
optimised away all of the type information by run time.
(But then again, I'm not really an OCaml hacker. Maybe you know a good
solution?)
There are several solutions. You can use higher-order functions:
module LoggedStack = struct
include Stack
let pop ?(print=fun _ -> ()) stack =
let elt = Stack.pop stack in
print elt;
elt
end;;
or modules (functors) to pass in a suitable print function from another
module:
module type PRINTER = sig
type t
val print : t -> unit
end;;
module LoggedStack(Printer : PRINTER) = struct
include Stack
let pop stack =
let elt = Stack.pop stack in
Printer.print elt;
elt
end;;
So I
modified the constructor to accept an additional function tos that
can convert an element into a string.
--8<---------------cut here---------------start------------->8---
class ['a] stack tos =
object (self)
val tos = tos;
val mutable list = ( [] : 'a list ) (* instance variable *)
method push x = (* push method *)
list <- x :: list
method pop = (* pop method *)
let result = hd list in
list <- tl list;
Printf.printf "%s\n" (tos result);
result
method peek = (* peek method *)
hd list
method size = (* size method *)
length list
end;;
--8<---------------cut here---------------end--------------->8---
It would be used like this:
--8<---------------cut here---------------start------------->8---
let _ =
let i = new stack string_of_int in
let ls =
let string_of_tupel t =
String.concat "" ["("; String.concat ", " [(fst t); (snd t)];
")"]
in
new stack string_of_tupel
in
i#push 1;
i#push 2;
ls#push ("one", "1");
ls#push ("two", "2");
ls#pop;
ls#pop;
i#pop;
i#pop
;;
--8<---------------cut here---------------end--------------->8---
This is not very elegant.
The functor approach makes better use of static typing but a generic print
will not be as elegant in OCaml as it is in Lisp.
I guess this could be seen as an instance of wanting to do Lisp in
OCaml. Maybe a seasoned OCaml programmer wouldn't want to do that
kind of thing in the first place.
Other MLs (e.g. F#) carry run-time type information and implement print_any,
which does what you want. However, they are as slow as Lisp...
The thing is, "polymorphic"
functions like repr/prin1 appear everywhere in a dynamically typed
language. In Gnus, e. g., a news and mail client for Emacs, there are
lots of places where you can either use a regular expression, a list
of regular expressions or a function. Here is an extract from the
Gnus docs:
--8<---------------cut here---------------start------------->8---
nnimap-split-rule
New mail found in /nnimap-split-inbox/ will be split according to
this variable.
This variable contains a list of lists, where the first element in
the sublist gives the name of the IMAP mailbox to move articles
matching the regexp in the second element in the sublist. Got
that? Neither did I, we need examples.
(setq nnimap-split-rule
'(("INBOX.nnimap"
"^Sender: owner-nnimap@xxxxxxxxxxxxxxxxxx")
("INBOX.junk" "^Subject:.*MAKE MONEY")
("INBOX.private" "")))
This will put all articles from the nnimap mailing list into
mailbox INBOX.nnimap, all articles containing MAKE MONEY in the
Subject: line into INBOX.junk and everything else in
INBOX.private.
[...]
The first element can also be the symbol junk to indicate that
matching messages should simply be deleted. Use with care.
The second element can also be a function. In that case, it will
be called with the first element of the rule as the argument, in a
buffer containing the headers of the article. It should return a
non-nil value if it thinks that the mail belongs in that group.
--8<---------------cut here---------------end--------------->8---
However, this style of programming goes against the grain of a
statically typed language. I like programming this way, which is why
I prefer dynamically typed languages like Lisp.
Not at all. When you said "a regular expression, a list of regular
expressions or a function", you were literally enumerating the constructors
of a variant type:
type ('a, 'b) t =
| RegExp of regexp
| RegExps of regexp list
| Fn of ('a -> 'b);;
Now that we've defined that type you can define your nnimap_split_rule
function to accept a value of this type (you can probably do more by
specifying the type of the function). Then the type is explicit, so you
don't need documentation that says things like "Got that? Neither did
I...", and your types will be automatically and statically checked for you.
Basically, you get machine-verified documentation.
PS: I had a look at your Ray tracer language comparison site. I was
very surprised to read that the Lisp programs were much more verbose
than the OCaml versions.
Indeed, Lisp is about the slowest and most verbose language on that test.
However, I believe the results are quite specific to the task in that
respect. Lisp should do a lot better on tasks where performance is
irrelevant or not CPU-limited and where EVAL is useful. I don't believe
(decent) static typing is ever enough of a hindrance that it will offset
Lisp's missing features, so trying to find tasks well suited to dynamic
typing is a red herring (IMHO).
So if I wanted to make Lisp look relatively better then I'd try to write an
efficient interpreter. I'd like to see a Lisp equivalent of the interpreter
that I've put up here, for example:
http://www.ffconsultancy.com/free/ocaml/interpreter.html
Something to think about ... I guess I should write a pattern matching
macro.
Yes. Having no form of pattern matching is just embarassing. Without variant
types there isn't a lot you can do with pattern matching.
--
Dr Jon D Harrop, Flying Frog Consultancy
Objective CAML for Scientists
http://www.ffconsultancy.com/products/ocaml_for_scientists
.
- Follow-Ups:
- Re: Lisp dinamism vs ML safety
- From: Wolfram Fenske
- Re: Lisp dinamism vs ML safety
- References:
- Re: a potential lisp convert, and interpreting the shootout
- From: Henry Bigelow
- Re: a potential lisp convert, and interpreting the shootout
- From: Jon Harrop
- Re: a potential lisp convert, and interpreting the shootout
- From: Henry Bigelow
- Re: a potential lisp convert, and interpreting the shootout
- From: Rahul Jain
- Re: a potential lisp convert, and interpreting the shootout
- From: Jon Harrop
- Re: a potential lisp convert, and interpreting the shootout
- From: Rahul Jain
- Re: a potential lisp convert, and interpreting the shootout
- From: Javier
- Re: a potential lisp convert, and interpreting the shootout
- From: Rahul Jain
- Re: a potential lisp convert, and interpreting the shootout
- From: Javier
- Re: a potential lisp convert, and interpreting the shootout
- From: Thomas A. Russ
- Lisp dinamism vs ML safety
- From: Javier
- Re: Lisp dinamism vs ML safety
- From: Alan Crowe
- Re: Lisp dinamism vs ML safety
- From: Javier
- Re: Lisp dinamism vs ML safety
- From: Wolfram Fenske
- Re: Lisp dinamism vs ML safety
- From: Javier
- Re: Lisp dinamism vs ML safety
- From: Wolfram Fenske
- Re: Lisp dinamism vs ML safety
- From: Jon Harrop
- Re: Lisp dinamism vs ML safety
- From: Wolfram Fenske
- Re: a potential lisp convert, and interpreting the shootout
- Prev by Date: Re: Lisp dinamism vs ML safety
- Next by Date: Which map function?
- Previous by thread: Re: Lisp dinamism vs ML safety
- Next by thread: Re: Lisp dinamism vs ML safety
- Index(es):
Relevant Pages
|