Ganymede-0.0.0.4: AmalObs.txt
Observations on Amalthea:
(Notes on Martin Sandin's Io interpreter, his OCaml style, and on OCaml itself.)
I'm "iffy" on OCaml after this. On the one hand, it seems quite able for writing good programs,
it has an easy way to use C object files, you can use OO programming when you need to, and pro-
cedural programming when you need it. Perhaps that's part of the problem; if you can program
the same old way that you've always done, then where's the incentive to learn how to program
in ways that can do things you can't do normally? Not that Haskell-like "Edelheit Über Alles"
(Purity Over All) is the answer, but...if your types can warn you ahead of time that you're
accessing varying parts of the system, it is helpful to know about it beforehand.
Some of the choices of style seem quirks of Sandin's and others seem quirks of the language.
The end result gives a feeling of not-so-fun quirkiness. I cannot tell if the way the
interpreter is structured was due to some unmentioned, impending requirement, raw "do what
works" thought processes, the influence of Sandin's OCaml teaching style, encouraged language
style conventions, or what, but there are many areas where the design feels uncomfortably
lacking in "separation of concern"-awareness or care. Maybe a rewrite was coming; the version
number is pre-1.0, after all. It just feels as if some things were done in an overly-compli-
cated way. I'm (2/10/11) still translating, so it'll have to wait until at least the first
rewrite on my part before a truer perspective can be gained. Part of the problem may be that
the five-to-ten years between when Amalthea was written and when "Project Ganymede" started
was a significant time of development, not just in Haskell, but in program design, especially
functional design.
Much of the issues gained here seem to be from ML's, especially OCaml's, half-hearted devotion to
design issues. Many (OCaml) language libraries (e.g., Arg, Parser, Lexer) seem to have a polished
API, while others (List, I'm looking at you) feel half-way done. Perhaps they just suffer in
comparison with Haskell's outstanding efforts over the intervening decade.
Discrepancies between Ganymede and Amalthea behavior:
1) Ganymede uses Haskell's GetOpt module, so when it prints an error, it has an odd-looking
paren on the last output line; Amalthea's error report isn't chopped up that way.
2) When multiple "pre-processing" options are passes to Amalthea, it only does one, "chosen" by
the order the argument clauses are written; Ganymede is written to do them all, if possible.
3) Amalthea's pretty print facility adds an "import prelude." to the program, whether it needs
one or not. Ganymede checks the program to see if it has a prelude statement.
4) In the spirit of "a motion to adjourn is always in order", Ganymede will never throw an
error when given a 'terminate' action; Amalthea checks the stack to be sure it's empty.
5) When run, Amalthea's usage blurb is output on stderr; also, when given no quick-exit options
or an input filename, the error message (of no sourcename) is output on stdout. Ganymede
does not copy that behavior, but outputs the error messages on stderr, and information
on stdout.
6) In the documentation, Martin Sandin remarks that the reason for including mutable variables
is "to support implementations of microthreads, coroutines, exceptions, and similar control
structures" - except that Raph Levien's paper also supported threads, coroutines and thread
messaging without exposing mutable variables to the notation syntax. Plus, they complicate
the interpreter, and reasoning about it.
7) Typical to OCaml, in fact to most of the programming industry, Amalthea uses separate
scanner/lexer and parser constructs. Haskell can (Alex/Happy) do so, but (Parsec) the more
idiomatic approach is to parse the input directly.
8) Similar to point 7, in OCaml, as I understand it, the parser holds a declarative description
of the parsing engine (on the tokens), while the Lexing module and the lexer itself deals with
the input stream, the file cursor (the 'pos' type) and tokenization. Parsec does it all in one
idiomatic package.