Commits

Anonymous committed 7819c13

Fix line-endings and file modes under `doc/`.

Comments (0)

Files changed (6)

doc/Design_Decisions.markdown

-Design Decisions
-================
-
-The design space for programming languages is monstrously large.  When you
-are designing an esolang, you have the luxury of narrowing down the design
-space, essentially arbitrarily, and focusing on a handful of computational
-gimmicks, and how they interact.
-
-In the process of designing a more "real" language, however, you have no
-such luxury -- ideally, all of your choices should have reasons behind them.
-There are no "right" decisions, of course, but the choices should be
-justifiable, given some set of goals.
-
-Perhaps more importantly, all of the reasons should be coherent, when taken
-together -- the same justifications should support all of them.  There is no
-point in justifying one decision with "it should be simple to implement
-instead of simple to program in", and another with just the opposite -- the
-end result will be a hodge-podge, and we might as well have just made our
-choices arbitrarily, without any justification at all.
-
-Having had to make the design decisions behind Robin, I will try to document
-the major ones here, and the reasons behind them.  Of course, since Robin's
-design is still under development, many of these are subject to change.
-
-Meta-Design
------------
-
-#### Should Robin be rigorously specified?
-
-Decision: Absolutely.
-
-A rigorous specification of a language allows two things:
-
-* Proofs of properties of programs in the language.  Without a formal
-  semantics, this just isn't possible.  It should also be standardized
-  (that is, there should be an "official" definition): it's all well and
-  good to independently define "a formal semantics" for some programming
-  language, but if different programmers are using different formal
-  semantics for the same language, they can't exchange their proofs.
-
-* Commodification of implementations of the language.  Allowing
-  implementors to independently implement the same language leads to a
-  "marketplace" of implementations, which (under prevailing economic
-  theories, anyway) leads to higher quality implementations through
-  competition.
-
-#### Should Robin's core language have a simple definition?
-
-Decision: Yes.
-
-Keeping the definition simple contributes to the same commodification goal
-listed above: it lowers the barriers to implementation.
-
-Providing a lot of useful things in the core does make some things handier
-for the programmer, but it does increase the effort to implement the
-langage (think of all the nooks and crannies of Perl.)  Instead, all these
-handy things should be packages in modules, which need not always be
-imported or used.
-
-Approaching this naively can lead to inefficiencies, however, as more
-advanced functionalities must be built up from simpler functionalities,
-and the "sufficiently clever compiler" that can optimize these is hard to
-come by.  So, if there are any measures we can take to mitigate this
-effect, without destroying simplicity -- we should investigate them.
-
-#### Should Robin be defined with denotational semantics?
-
-Decision: No.
-
-It's inaccessible to most programmers, and it is essentially just
-another programming language, which is itself not perfectly well
-standardized.
-
-A much better choice is the programming language Haskell.  It is
-quite well defined, quite close to denotational semantics (in its
-pure form, anyway), and above all, executable -- leading immediately
-to a usable reference interpreter.
-
-#### Should Robin be defined using multiple definition languages?
-
-Decision: Yes.
-
-The method of description should employ at least two descriptions in two
-language-describing languages.  This way, a form of "error-detecting
-code" applies: each description can be checked for consistency against the
-other.  (Using three languages would permit a form of "error-correcting
-code": whichever behavior is in at least two of the descriptions is
-considered official, and the third is considered erroneous.  But this is
-possibly too burdensome in practice.)
-
-Given Haskell as one of the definition lanaguges, the logical choice here
-is Literate Haskell, with each part of the Haskell definition accompanied
-by a definition in (relatively formal) English.
-
-#### Should the language also be defined with conformancy tests?
-
-Decision: Yes.
-
-Of course, it's very difficult to compose tests which actually define a
-language.  You can't effectively test that such-and-such a program leads
-to an infinite loop, and you can't effectively test that such-and-such a
-program has the same behaviour on *any* of an infinite possible set of
-inputs.
-
-But, you can write tests that detect a finite number of points where an
-erroneous implementation fails to meet the definition.  And, you can
-execute these tests on a computer -- in the process of developing a new
-implementation, this can help a lot.  And, this brings the definition
-closer to being "in triplicate" and thus having some properties of an
-error-corrcting code.  So, conformancy tests should definitely be part of
-the language's documentation.
-
-Design Proper
--------------
-
-#### Should Robin be a general-purpose language?
-
-Decision: Yes.
-
-Not much to say about this.  A general-purpose language with a module
-system, not to mention macros, can be customized for specific purposes.
-(We need to do more investigation into how "teleological contexts" and
-metadata on values can be leveraged for such purposes.)
-
-#### Should Robin's syntax be based on S-expressions?
-
-Decision: Yes -- but it should not be the *only* syntax.
-
-Robin, as it stands currently, is a "sugar-free" language.  Programs and
-modules are represented concretely as S-expressions, which typically map
-directly to the AST (abstract syntax tree) used by the implementation.
-
-Research in linguistics suggests there is such a thing as too much
-regularity in a language for human comfort.  All spoken languages have
-some irregularity in them.  When constructed languages such as Esperanto
-are taught as native languages to children, they tend to be "irregularized"
-as they are acquired.  Perhaps the human mind needs these irregularities as
-"handles" to better grasp the ways to express concepts, or perhaps it uses
-them as "checksums" for error correction and disambiguation -- but these
-are just pet theories.  Whatever the reason is, it happens.
-
-My point is, S-expression-based languages are certainly a formal instance
-of language structure which is "too regular for comfort", so programming
-in Robin (or Scheme, or Lisp) often tends to be somewhat brutal (especially
-without editor support to match parentheses for you.)
-
-However, mathematically and in software engineering, this regularity
-provides immense benefits, because it both makes the structure of
-the language simple, and thus easy to define and analyze, and makes the
-language very expressive -- the ease of writing code that works on code
-makes it possible to create very flexible and coherent (I daresay
-"powerful") abstractions.  So, Robin errs on the side of this benefit.
-
-However, there is no reason that Robin should fixate on this syntax.
-It is important not to neglect usability, and, although one has not yet
-been devised, there is no reason that Robin cannot have other, more "humane"
-alternate syntaxes which are easier to read and write.
-
-A sugared "humane" syntax might look like the following.
-
-    robin 1.0
-    import small 1.0
-
-    pi = 3.14159
-
-    fac(x) =
-        if x <= 1 then 1 else
-            r = fac(x - 1)
-            r * x
-        end
-
-    fac(7) * pi
-
-It would be translated by a pre-processing step to something like:
-
-    (robin (1 . 0) (small (1 . 0))
-      (bind pi 314159/100000
-        (bind fac (lambda (self X)
-          (if (<= x 1)
-            1
-            (bind r (self self (- x 1)) (* r x))))
-          (* (fac fac 7) pi))))
-
-#### What should be in the core?
-
-Decision: A semantically minimal set of macros.
-
-I went back and forth before deciding what should be in the core and
-why.  One possibility was to make it the same as Pixley.  But macros
-would be added to it, and macros would need to be in the core (as they
-can't be written directly in Pixley), and once you have macros, a lot
-of the Pixley functions, like `let*` and `cond`, *can* be written in
-the language.  So, should they remain in the core?
-
-I decided no: the core would be simpler to implement and analyze
-without them.
-
-The only place where I waver on this currently is `fun`.  While `fun`
-*can* be defined as a macro, it is so basic to writing modules in
-Robin, that it is very tempting to place it in the core.  (The version
-defined as a macro is very inefficient, but of course the `small`
-module need not be implemented in Robin itself.)
-
-#### Should all programs be contained in some kind of header form?
-
-Decision: Yes.
-
-We want to be able to quickly identify what S-expressions are
-Robin programs, and what aren't, especially if we're using some of the
-same identifiers as other languages, like Scheme.  Also, this is a
-good place to specify the version of Robin in use, and a good place
-to import required modules.
-
-An alternative idea was some kind of meta-format called "Parts":
-
-    (parts (import (robin 1 0) ...)
-
-But "Parts" would not establish the deep semantics of the language
-(reduction order, etc.)  And subsequent imports might rely (heavily)
-on those semantics.  Meaning, imports would have to import fundamental
-semantics, and imports would depend on that being imported first,
-and, the result is just ugly.
-
-#### Should you have to import the core?
-
-Decision: Yes.
-
-This is actually a special case of a more general design decision,
-namely:
-
-#### Should modules be fine-grained?
-
-Decision: Yes.
-
-If modules are fine-grained, and only a few are truly required, the task
-of implementing (or porting) the language is much simpler.
-
-This applies as well to architectures that don't support all functions
-in all modules.  For example, clockless systems won't have a way to
-retrieve the current time of day.  *But*, such systems would still be
-capable of manipulate date and time values.  Therefore, those two sets
-of functions, though closely related, should not be bundled into the
-same module.
-
-It's true that it's annoying for the programmer to remember which
-module a function is in.  For this reason, we can have "umbrella modules"
-which simply re-export all the functions in a large set of standard
-modules -- assuming there are no name conflicts amongst them.
-
-More philosophically: if something is part of the core semantics of
-the language (like error codes,) should it be put in a module?  Largely
-I've been able to arrange things to avoid this issue.  For example, if
-`head` fails, it raises an exception if the implementation supports
-exceptions, otherwise it just aborts execution.  But, when when support
-for exceptions exists, if a raised exception is not caught, execution
-is aborted -- so the behaviour is compatible.  However, there are
-potentially other instances of "semantics for this are in the core, but
-you have to import this module to get at thim" -- I've seen them in
-other languages, and when I remember or re-find an example of it, I'll
-add it here.
-
-#### Should importing be done in the header, or by a function?
-
-Decision: In the header.  
-Chance of changing: Non-zero.
-
-Importing modules in the header is a form of statically declaring the
-dependencies of a program; if one of the modules isn't available on
-some system, it can instantly say "no, I can't run this."
-
-If there was instead a function to import modules, such a system would
-need to statically analyze the program to see if dependencies are met
-(see Python's `setuptools`).  When it can't figure that out exactly,
-which is inevitable, the program will break at some arbitrary point
-during execution.
-
-Also, importing via a function would require that the function to do
-the importing would be exported before everything else; in other words,
-`(robin (1 0) ...)` would need to export one function, `import`.  This
-is slightly un-orthogonal.
-
-The downside of statically declaring the modules in the header is that
-you might want to write a program which is somewhat flexible: if a
-particular module is available, it will take advantage of it, but if not,
-it will fall back to something perhaps less optimal but still usable.
-You can't do that in the current regime.
-
-However, there may be better ways to think about this, and they go back
-to ideas I had about Robin when it was in my mind more like an operating
-system.  The issue is often not the availability of a module but rather
-the availability of a resource; modules are, at best, definitions,
-rather than suppliers, of resources.  But, I will have to think about
-this more.
-
-#### Should function names follow in the Lisp/Scheme tradition?
-
-Decision: No.
-
-It's good to have roots, but there are limits.
-
-Lisp/Scheme names posess a lot of awfulness due to their legacy.
-`cdr` absolutely sucks as a name.  Unfortunately, things like `tail`
-and `snd` aren't standard replacements for it, yet.  `lambda` is
-less offensive, but only because it's a widespread standard; there is
-nothing except Church's work that ties the Greek letter lambda to
-the idea of a function, and even that is, if you believe the folklore,
-mainly due to typesetting limitations he encountered in publishing.
-
-Just because programmers are familiar with a notation or concept is not
-enough of a reason to incorporate it into the language's foundation. At
-the same time, we'd obviously prefer not to alienate programmers
-completely (that's what esolangs are for!)
-
-If the programmer really wants Lisp/Scheme names, they can always
-define them in a "compatibility module".  (In fact, I should probably
-anticipate this, and accomodate it with an established convention.)
-
-#### Should `#t` and `#f` be Church booleans?
-
-Decision: No.
-
-While it's tempting in that it would allow us to not have `if` in the
-core, it just moves that complexity from `if`, a built-in macro, to
-the evaluator and/or type system.  Having an explicit, separate `if`
-lets `#t` and `#f` be more like plain symbols.  In fact, one day, they
-might be classified as such -- if I can grapple other design decisions
-in the way of that.
-
-#### Should Robin allow improper lists?
-
-Decision: No.
-
-Drawing directly from the Lisp/Scheme tradition, and being supported by the
-idea that the core semantics should admit as much "goo" as possible ("it's
-not a language so much as it's a building material"), with static analysis,
-if desired, being layered on top of that, improper lists were originally
-allowed in Robin.
-
-However, there are several points that can be made against them, so they
-were removed from the language.
-
-* We may want to base everything on "goo", but we should want clean "goo".
-
-* You can always simulate an improper list with a proper list with some
-  kind of marker term at the end.
-
-* The very name "improper" should be a big hint that these constructs are
-  not clean.  (However, this argument could be regarded as sophistry.)
-
-* Various functions in the `list` module currently have slightly different
-  behaviour on proper versus improper lists.  Proper lists only would make
-  them more orthogonal.
-
-* Improper lists maybe have a place in history; when resources like memory
-  were scarce, they were a way of saving a cons cell.  However, this now
-  goes against treating resources as not scarce in order to have a more
-  abstract and elegant description of programs.
-
-* When you have both proper and improper lists, `list?` is O(n); with only
-  proper lists, `list?` is O(1), basically the same as `pair? or null?`.
-
-#### Should we require lists in syntax where they aren't strictly necessary?
-
-Decision: Yes.
-
-What do I even mean by this?  Well, for example, Scheme's `let*` requires
-that you put all the bindings in a list:
-
-    (let* ((a 1)
-           (b 2)
-           (c 3))
-       (foo a b c))
-
-That intermediate list isn't really necessary; the implementation of `let*`
-could just treat the last term as the expression to be evaluated in the new
-environment:
-
-    (let* (a 1)
-          (b 2)
-          (c 3)
-      (foo a b c))
-
-This is good under the theory "the fewer parentheses, the better", and this
-is not a bad theory.  Also, it is perhaps less efficient (because the
-implementation must look ahead to see if something is a binding or not), but
-again, resources should not be considered scarce; it can always be converted
-internally to something more efficient.
-
-But, Robin will one day have a more humane syntax, so that programmers won't
-have to deal with these forms unless they want to.  The intermediate list
-could also be seen as more orthogonal to the semantics (you really are
-working with a list of bindings, and you shouldn't overload the meanings of
-things in the list.)
-
-So, Robin's `let*` does have an intermediate list.  (On the other hand,
-`bind` doesn't need a list at all, obviating the issue.)  Following suit,
-the syntax for importing modules uses a list to contain the module specifiers
-(although it did not originally.)
-
-#### Should the language define static analyses?
-
-Decision: No, but it should accomodate them.
-
-This is a pretty subtle issue, which is explained more fully in the
-Static Analysis document.  But in short, to work towards the goal of
-keeping the language simple, we want to move things out of it, to modules
-and/or to implementation issues (such as configuration files), and one
-of the things we can move out is static analysis.
-
-At the same time, the language should be designed to accomodate static
-analyzers that are built on top of it, and some of those static analyzers
-should be standard.
-
-A language can define a type system without specifying that types should
-be checked statically.  However, if no thought is put into how easily the
-types of a program can be statically analyzed, this raises barriers to
-actually doing it.  Static analyzers in the world of scripting languages
-in particular are often an afterthought, and we want to try to minimize
-that effect.
-
-#### How should serializability be handled?
-
-Decision: ...
-
-OK, so: one reason to choose an S-expression based syntax is that terms
-so represented are, for the most part, "trivially serializable", because
-many forms simply evaluate to themselves, and when they don't, they can
-be wrapped in `literal`.
-
-This is useful because, where-ever values are serialized (disk files,
-messages between nodes, etc.,) they look just like they would in a program
-text.
-
-However, there is an inherent tension between concrete representations and
-abstract types.
-
-In the following, _erroneous access_ means transformations of data that
-result in a non-conformant structure.  _Interchangeability_ means only
-allowing a set of operations whose implementations can be changed.
-
-Concrete representations serialize trivially, and can be pattern-matched,
-but they do not prevent erroneous access, nor support interchangeability.
-
-Abstract types prevent erroneous access and support interchangeability,
-but they do not serialize trivially, nor can they be pattern-matched.
-
-(Abstract types are also traditionally desirable because they allow
-optimizations, but since performance is a non-goal of Robin, we won't
-discuss that here.)
-
-A solution is to ensure every abstract type has two operations, `to-repr`
-and `from-repr`, which convert the abstract value into a concrete
-representation and vice-versa.  `to-repr` should be deterministic; for all
-values _v_ of some abstract type _t_, all implementations of _t_ should
-produce the same value for `to-repr` _v_.  For example, an abstract type
-of dictionaries might have as its representation a sorted, non-redundant
-alist.
-
-This permits serialization, pattern-matching, equality testing, etc.,
-simply by (implicitly) calling `to-repr` on the value first.
-
-These two functions should be round-trippable, in that for all _v_,
-`(from-repr (to-repr v))` = v.  Some information may be lost, but
-such information should not be critical information (e.g. caching most
-recently used values for the sake of performance, etc.)
-
-You can still try to perform erroneous access by converting the abstract
-value to a concrete representation, mucking with the representation, then
-converting it back to the abstract value.  However, the representation
-should be defined at the level of properties of the abstraction, and trying
-to convert it to an abstract value should raise an exception if those
-properties do not hold (i.e. if the concrete value is "corrupt".)
-
-We could treat macro values as abstract values.  The representation of a
-macro value is its definition, plus any values that might be closed over in
-it (represented as a `let` wrapping the definition.)  But there is one
-desired property that does not hold -- the representation of a macro is not
-deterministic; there are an infinite number of equivalent representations of
-any macro, and no effective procedure to select the "simplest" one (or any
-other way to effectively order possible representations.)
-
-We could treat pids as abstract values.  One might hope to do information
-hiding with pids; you should be unable to send a message to a process unless
-you got its pid from it or its parent (perhaps indirectly).  However,
-`from-repr` lets you make up pids "from scratch" (from arbitrary concrete
-representations) and even if the pid structures contains an obscure
-fingerprint or the like, you might accidentally hit upon an existing pid.
-
-However, maybe that is not so bad; we could call it the "how did you get
-this phone number" problem.  Even if pids are abstract, a process can't
-really rely on its parent not somehow sharing its pid with some process it
-knows nothing about, barring some really involved proofs.  And, even with an
-abstract pid, you can't guarantee e.g. that sending it a message has some
-meaning; the process might have died, maybe long enough ago that the pid was
-recycled and a new process lives there now.  (Though, of course, we should
-take measures to reduce the chances of all these things.)
-
-Can we implement abstract values as functions?  Take a queue for instance:
-
-    (bind q (queue:empty)
-      (((q enqueue 33) enqueue 66) deqpeek))
-
-...should evaluate to 33.  This is pretty good.  What would the
-implementation look like?
-
-    (bind empty
-      (bind contents ()
-        (macro (self args env)
-          ; if args#0 is 'enqueue', return a macro like 'self'
-          ; closed over (pair args#1 contents)
-          ; else if args#0 is 'deqpeek', return (list:last contents)
-        )
-
-But it looks like this might involve mucking with the closed-over
-environment of the macro -- which we could make possible.  But I'm not
-sure we want to.  Anyway, this macro would also need to implement the
-operations `(q to-repr)` and `(q from-repr (list 1 2 3))`.  The latter
-is actually a "class method" and doesn't need to be on an existing
-instance; but that is its own, fairly involved design decision.
-
-Also, Robin potentially has the ability to use different implementations
-(representations) of the same abstract data type in different modules,
-independent of the program: the module configuration file could point
-one module to an alist-backed dictionary data type, and another module
-at a hash-table-backed dictionary data type.  Those two modules ought to
-still be able to pass objects of the dictionary data type back and forth
-to each other.
-
-#### Should environments be abstract data types?
-
-Decision: Currently they aren't, but they should be.
-
-Currently, all macros in the standard modules accept and return alist
-representations of environments.  But there are mismatches between this
-representation, and what environments actually support.  There can be
-multiple entries for the same key in an alist, and alists expose an order
-to their entries, neither of which is a necessity of environments.  There
-are potentially many ways to represent an environment.  So, environments
-should be encapsulated in an abstract data type that supports the
-operations `lookup` and `extend`.
-
-#### Should strings be abstract data types?
-
-Decision: Again, currently they aren't, but they should be.
-
-Again, there are multiple possible ways to represent a string: a naive
-list of integers (representing characters) suffices for many applications,
-but other applications may benefit from using a "rope" representation, or
-some other representation.
-
-#### Should there be a distinction between processes and devices?
-
-Decision: Yes.
-
-It's tempting to unify the two, and say that there are only devices,
-and that every concurrent process you spawn is a device of some sort.
-
-But devices represent resources beyond just cycles and memory, while
-you can have a "compute" process which just uses cycles and memory.
-It doesn't need to be acquired, or released, or registered for access
-by some other, anonymous program.
-
-#### Should the `random` facility be a device?
-
-Decision: No.
-
-Following the decision immediately above, and the decision to have
-fine-grained modules -- the `random` module itself is simply doing
-computation (generating ever-more pseudo-random numbers from a seed.)
-*However*, it may be seeded with a source of entropy from the system --
-which implies that there should, indeed, be an `entropy` device.
-
-But considering naming conventions -- possibly, for the same reason,
-the `random` module should be called `pseudo-random` or similar.  And
-`entropy` might likewise be better named `random`.  Not sure.
-
-#### Should all messaging transactions be synchronous?
-
-Decision: It's tempting, but I'm starting to think it's not practical.
-
-While the Erlang paradigm for message-passing is very simple -- just
-`Pid ! msg` and you've sent a message -- it's also very low-level.  For
-Erlang/OTP, things like `gen_server` are built on top of this, using
-sets of functions to abstract away the details.  Robin does something
-similar with `call!` and `respond!`.  This lets you write code where you
-can be reasonably sure that the other processes you are sending messages
-to are in the state that you expect.
-
-Being able to send a message to a process and not expect a reply does
-let you write potentially more efficient code; you don't have to wait for
-your message to be acknowledged.  But combining this with `call!`/`respond!`
-can lead to complex patterns of communication, where it is difficult to
-reason about whether the other processes are in the state you expect.
-(If the server process uses `respond!`, what should it do about messages
-that don't require an acknowledgement?  How sophisticated does `respond!`
-need to be?)
-
-Also, requiring synchronous communication between processes does not
-preclude asynchonous processing -- it's just that your "start doing this"
-message needs to be acknowledged by the other process, and your process
-needs to wait for that acknowledgement.  The activity itself still goes
-on independent of the current process.
-
-Also, race conditions are among the hardest bugs to detect, isolate and
-fix, and unacknowledged asynchronous messages probably lead to them (though
-not so much as shared updatable storage leads to them.)  Doing what we can
-to encourage programmers to avoid race conditions in design is probably
-called for.
-
-However... the problem is that if all messaging is synchronous, you lose
-one of the main benefits of messaging, which is (this sounds tautological)
-asynchronicity.  If you always need to wait for another process to confirm
-that it got your message, you can't do anything else in the meantime.
-
-Perhaps there is a way around this.  I'll need to come up with some
-examples and write them down here.
-
-#### Should all messaging consist of message structures (with envelopes)?
-
-Decision: Again, not sure, leaning towards yes.
-
-Again, `Pid ! Msg` where `Msg` is anything is very simple, but again, it
-is very low-level.  It is useful to have metadata about the message, like
-which process sent it, and when.  But I need to think about this more.
+Design Decisions
+================
+
+The design space for programming languages is monstrously large.  When you
+are designing an esolang, you have the luxury of narrowing down the design
+space, essentially arbitrarily, and focusing on a handful of computational
+gimmicks, and how they interact.
+
+In the process of designing a more "real" language, however, you have no
+such luxury -- ideally, all of your choices should have reasons behind them.
+There are no "right" decisions, of course, but the choices should be
+justifiable, given some set of goals.
+
+Perhaps more importantly, all of the reasons should be coherent, when taken
+together -- the same justifications should support all of them.  There is no
+point in justifying one decision with "it should be simple to implement
+instead of simple to program in", and another with just the opposite -- the
+end result will be a hodge-podge, and we might as well have just made our
+choices arbitrarily, without any justification at all.
+
+Having had to make the design decisions behind Robin, I will try to document
+the major ones here, and the reasons behind them.  Of course, since Robin's
+design is still under development, many of these are subject to change.
+
+Meta-Design
+-----------
+
+#### Should Robin be rigorously specified?
+
+Decision: Absolutely.
+
+A rigorous specification of a language allows two things:
+
+* Proofs of properties of programs in the language.  Without a formal
+  semantics, this just isn't possible.  It should also be standardized
+  (that is, there should be an "official" definition): it's all well and
+  good to independently define "a formal semantics" for some programming
+  language, but if different programmers are using different formal
+  semantics for the same language, they can't exchange their proofs.
+
+* Commodification of implementations of the language.  Allowing
+  implementors to independently implement the same language leads to a
+  "marketplace" of implementations, which (under prevailing economic
+  theories, anyway) leads to higher quality implementations through
+  competition.
+
+#### Should Robin's core language have a simple definition?
+
+Decision: Yes.
+
+Keeping the definition simple contributes to the same commodification goal
+listed above: it lowers the barriers to implementation.
+
+Providing a lot of useful things in the core does make some things handier
+for the programmer, but it does increase the effort to implement the
+langage (think of all the nooks and crannies of Perl.)  Instead, all these
+handy things should be packages in modules, which need not always be
+imported or used.
+
+Approaching this naively can lead to inefficiencies, however, as more
+advanced functionalities must be built up from simpler functionalities,
+and the "sufficiently clever compiler" that can optimize these is hard to
+come by.  So, if there are any measures we can take to mitigate this
+effect, without destroying simplicity -- we should investigate them.
+
+#### Should Robin be defined with denotational semantics?
+
+Decision: No.
+
+It's inaccessible to most programmers, and it is essentially just
+another programming language, which is itself not perfectly well
+standardized.
+
+A much better choice is the programming language Haskell.  It is
+quite well defined, quite close to denotational semantics (in its
+pure form, anyway), and above all, executable -- leading immediately
+to a usable reference interpreter.
+
+#### Should Robin be defined using multiple definition languages?
+
+Decision: Yes.
+
+The method of description should employ at least two descriptions in two
+language-describing languages.  This way, a form of "error-detecting
+code" applies: each description can be checked for consistency against the
+other.  (Using three languages would permit a form of "error-correcting
+code": whichever behavior is in at least two of the descriptions is
+considered official, and the third is considered erroneous.  But this is
+possibly too burdensome in practice.)
+
+Given Haskell as one of the definition lanaguges, the logical choice here
+is Literate Haskell, with each part of the Haskell definition accompanied
+by a definition in (relatively formal) English.
+
+#### Should the language also be defined with conformancy tests?
+
+Decision: Yes.
+
+Of course, it's very difficult to compose tests which actually define a
+language.  You can't effectively test that such-and-such a program leads
+to an infinite loop, and you can't effectively test that such-and-such a
+program has the same behaviour on *any* of an infinite possible set of
+inputs.
+
+But, you can write tests that detect a finite number of points where an
+erroneous implementation fails to meet the definition.  And, you can
+execute these tests on a computer -- in the process of developing a new
+implementation, this can help a lot.  And, this brings the definition
+closer to being "in triplicate" and thus having some properties of an
+error-corrcting code.  So, conformancy tests should definitely be part of
+the language's documentation.
+
+Design Proper
+-------------
+
+#### Should Robin be a general-purpose language?
+
+Decision: Yes.
+
+Not much to say about this.  A general-purpose language with a module
+system, not to mention macros, can be customized for specific purposes.
+(We need to do more investigation into how "teleological contexts" and
+metadata on values can be leveraged for such purposes.)
+
+#### Should Robin's syntax be based on S-expressions?
+
+Decision: Yes -- but it should not be the *only* syntax.
+
+Robin, as it stands currently, is a "sugar-free" language.  Programs and
+modules are represented concretely as S-expressions, which typically map
+directly to the AST (abstract syntax tree) used by the implementation.
+
+Research in linguistics suggests there is such a thing as too much
+regularity in a language for human comfort.  All spoken languages have
+some irregularity in them.  When constructed languages such as Esperanto
+are taught as native languages to children, they tend to be "irregularized"
+as they are acquired.  Perhaps the human mind needs these irregularities as
+"handles" to better grasp the ways to express concepts, or perhaps it uses
+them as "checksums" for error correction and disambiguation -- but these
+are just pet theories.  Whatever the reason is, it happens.
+
+My point is, S-expression-based languages are certainly a formal instance
+of language structure which is "too regular for comfort", so programming
+in Robin (or Scheme, or Lisp) often tends to be somewhat brutal (especially
+without editor support to match parentheses for you.)
+
+However, mathematically and in software engineering, this regularity
+provides immense benefits, because it both makes the structure of
+the language simple, and thus easy to define and analyze, and makes the
+language very expressive -- the ease of writing code that works on code
+makes it possible to create very flexible and coherent (I daresay
+"powerful") abstractions.  So, Robin errs on the side of this benefit.
+
+However, there is no reason that Robin should fixate on this syntax.
+It is important not to neglect usability, and, although one has not yet
+been devised, there is no reason that Robin cannot have other, more "humane"
+alternate syntaxes which are easier to read and write.
+
+A sugared "humane" syntax might look like the following.
+
+    robin 1.0
+    import small 1.0
+
+    pi = 3.14159
+
+    fac(x) =
+        if x <= 1 then 1 else
+            r = fac(x - 1)
+            r * x
+        end
+
+    fac(7) * pi
+
+It would be translated by a pre-processing step to something like:
+
+    (robin (1 . 0) (small (1 . 0))
+      (bind pi 314159/100000
+        (bind fac (lambda (self X)
+          (if (<= x 1)
+            1
+            (bind r (self self (- x 1)) (* r x))))
+          (* (fac fac 7) pi))))
+
+#### What should be in the core?
+
+Decision: A semantically minimal set of macros.
+
+I went back and forth before deciding what should be in the core and
+why.  One possibility was to make it the same as Pixley.  But macros
+would be added to it, and macros would need to be in the core (as they
+can't be written directly in Pixley), and once you have macros, a lot
+of the Pixley functions, like `let*` and `cond`, *can* be written in
+the language.  So, should they remain in the core?
+
+I decided no: the core would be simpler to implement and analyze
+without them.
+
+The only place where I waver on this currently is `fun`.  While `fun`
+*can* be defined as a macro, it is so basic to writing modules in
+Robin, that it is very tempting to place it in the core.  (The version
+defined as a macro is very inefficient, but of course the `small`
+module need not be implemented in Robin itself.)
+
+#### Should all programs be contained in some kind of header form?
+
+Decision: Yes.
+
+We want to be able to quickly identify what S-expressions are
+Robin programs, and what aren't, especially if we're using some of the
+same identifiers as other languages, like Scheme.  Also, this is a
+good place to specify the version of Robin in use, and a good place
+to import required modules.
+
+An alternative idea was some kind of meta-format called "Parts":
+
+    (parts (import (robin 1 0) ...)
+
+But "Parts" would not establish the deep semantics of the language
+(reduction order, etc.)  And subsequent imports might rely (heavily)
+on those semantics.  Meaning, imports would have to import fundamental
+semantics, and imports would depend on that being imported first,
+and, the result is just ugly.
+
+#### Should you have to import the core?
+
+Decision: Yes.
+
+This is actually a special case of a more general design decision,
+namely:
+
+#### Should modules be fine-grained?
+
+Decision: Yes.
+
+If modules are fine-grained, and only a few are truly required, the task
+of implementing (or porting) the language is much simpler.
+
+This applies as well to architectures that don't support all functions
+in all modules.  For example, clockless systems won't have a way to
+retrieve the current time of day.  *But*, such systems would still be
+capable of manipulate date and time values.  Therefore, those two sets
+of functions, though closely related, should not be bundled into the
+same module.
+
+It's true that it's annoying for the programmer to remember which
+module a function is in.  For this reason, we can have "umbrella modules"
+which simply re-export all the functions in a large set of standard
+modules -- assuming there are no name conflicts amongst them.
+
+More philosophically: if something is part of the core semantics of
+the language (like error codes,) should it be put in a module?  Largely
+I've been able to arrange things to avoid this issue.  For example, if
+`head` fails, it raises an exception if the implementation supports
+exceptions, otherwise it just aborts execution.  But, when when support
+for exceptions exists, if a raised exception is not caught, execution
+is aborted -- so the behaviour is compatible.  However, there are
+potentially other instances of "semantics for this are in the core, but
+you have to import this module to get at thim" -- I've seen them in
+other languages, and when I remember or re-find an example of it, I'll
+add it here.
+
+#### Should importing be done in the header, or by a function?
+
+Decision: In the header.  
+Chance of changing: Non-zero.
+
+Importing modules in the header is a form of statically declaring the
+dependencies of a program; if one of the modules isn't available on
+some system, it can instantly say "no, I can't run this."
+
+If there was instead a function to import modules, such a system would
+need to statically analyze the program to see if dependencies are met
+(see Python's `setuptools`).  When it can't figure that out exactly,
+which is inevitable, the program will break at some arbitrary point
+during execution.
+
+Also, importing via a function would require that the function to do
+the importing would be exported before everything else; in other words,
+`(robin (1 0) ...)` would need to export one function, `import`.  This
+is slightly un-orthogonal.
+
+The downside of statically declaring the modules in the header is that
+you might want to write a program which is somewhat flexible: if a
+particular module is available, it will take advantage of it, but if not,
+it will fall back to something perhaps less optimal but still usable.
+You can't do that in the current regime.
+
+However, there may be better ways to think about this, and they go back
+to ideas I had about Robin when it was in my mind more like an operating
+system.  The issue is often not the availability of a module but rather
+the availability of a resource; modules are, at best, definitions,
+rather than suppliers, of resources.  But, I will have to think about
+this more.
+
+#### Should function names follow in the Lisp/Scheme tradition?
+
+Decision: No.
+
+It's good to have roots, but there are limits.
+
+Lisp/Scheme names posess a lot of awfulness due to their legacy.
+`cdr` absolutely sucks as a name.  Unfortunately, things like `tail`
+and `snd` aren't standard replacements for it, yet.  `lambda` is
+less offensive, but only because it's a widespread standard; there is
+nothing except Church's work that ties the Greek letter lambda to
+the idea of a function, and even that is, if you believe the folklore,
+mainly due to typesetting limitations he encountered in publishing.
+
+Just because programmers are familiar with a notation or concept is not
+enough of a reason to incorporate it into the language's foundation. At
+the same time, we'd obviously prefer not to alienate programmers
+completely (that's what esolangs are for!)
+
+If the programmer really wants Lisp/Scheme names, they can always
+define them in a "compatibility module".  (In fact, I should probably
+anticipate this, and accomodate it with an established convention.)
+
+#### Should `#t` and `#f` be Church booleans?
+
+Decision: No.
+
+While it's tempting in that it would allow us to not have `if` in the
+core, it just moves that complexity from `if`, a built-in macro, to
+the evaluator and/or type system.  Having an explicit, separate `if`
+lets `#t` and `#f` be more like plain symbols.  In fact, one day, they
+might be classified as such -- if I can grapple other design decisions
+in the way of that.
+
+#### Should Robin allow improper lists?
+
+Decision: No.
+
+Drawing directly from the Lisp/Scheme tradition, and being supported by the
+idea that the core semantics should admit as much "goo" as possible ("it's
+not a language so much as it's a building material"), with static analysis,
+if desired, being layered on top of that, improper lists were originally
+allowed in Robin.
+
+However, there are several points that can be made against them, so they
+were removed from the language.
+
+* We may want to base everything on "goo", but we should want clean "goo".
+
+* You can always simulate an improper list with a proper list with some
+  kind of marker term at the end.
+
+* The very name "improper" should be a big hint that these constructs are
+  not clean.  (However, this argument could be regarded as sophistry.)
+
+* Various functions in the `list` module currently have slightly different
+  behaviour on proper versus improper lists.  Proper lists only would make
+  them more orthogonal.
+
+* Improper lists maybe have a place in history; when resources like memory
+  were scarce, they were a way of saving a cons cell.  However, this now
+  goes against treating resources as not scarce in order to have a more
+  abstract and elegant description of programs.
+
+* When you have both proper and improper lists, `list?` is O(n); with only
+  proper lists, `list?` is O(1), basically the same as `pair? or null?`.
+
+#### Should we require lists in syntax where they aren't strictly necessary?
+
+Decision: Yes.
+
+What do I even mean by this?  Well, for example, Scheme's `let*` requires
+that you put all the bindings in a list:
+
+    (let* ((a 1)
+           (b 2)
+           (c 3))
+       (foo a b c))
+
+That intermediate list isn't really necessary; the implementation of `let*`
+could just treat the last term as the expression to be evaluated in the new
+environment:
+
+    (let* (a 1)
+          (b 2)
+          (c 3)
+      (foo a b c))
+
+This is good under the theory "the fewer parentheses, the better", and this
+is not a bad theory.  Also, it is perhaps less efficient (because the
+implementation must look ahead to see if something is a binding or not), but
+again, resources should not be considered scarce; it can always be converted
+internally to something more efficient.
+
+But, Robin will one day have a more humane syntax, so that programmers won't
+have to deal with these forms unless they want to.  The intermediate list
+could also be seen as more orthogonal to the semantics (you really are
+working with a list of bindings, and you shouldn't overload the meanings of
+things in the list.)
+
+So, Robin's `let*` does have an intermediate list.  (On the other hand,
+`bind` doesn't need a list at all, obviating the issue.)  Following suit,
+the syntax for importing modules uses a list to contain the module specifiers
+(although it did not originally.)
+
+#### Should the language define static analyses?
+
+Decision: No, but it should accomodate them.
+
+This is a pretty subtle issue, which is explained more fully in the
+Static Analysis document.  But in short, to work towards the goal of
+keeping the language simple, we want to move things out of it, to modules
+and/or to implementation issues (such as configuration files), and one
+of the things we can move out is static analysis.
+
+At the same time, the language should be designed to accomodate static
+analyzers that are built on top of it, and some of those static analyzers
+should be standard.
+
+A language can define a type system without specifying that types should
+be checked statically.  However, if no thought is put into how easily the
+types of a program can be statically analyzed, this raises barriers to
+actually doing it.  Static analyzers in the world of scripting languages
+in particular are often an afterthought, and we want to try to minimize
+that effect.
+
+#### How should serializability be handled?
+
+Decision: ...
+
+OK, so: one reason to choose an S-expression based syntax is that terms
+so represented are, for the most part, "trivially serializable", because
+many forms simply evaluate to themselves, and when they don't, they can
+be wrapped in `literal`.
+
+This is useful because, where-ever values are serialized (disk files,
+messages between nodes, etc.,) they look just like they would in a program
+text.
+
+However, there is an inherent tension between concrete representations and
+abstract types.
+
+In the following, _erroneous access_ means transformations of data that
+result in a non-conformant structure.  _Interchangeability_ means only
+allowing a set of operations whose implementations can be changed.
+
+Concrete representations serialize trivially, and can be pattern-matched,
+but they do not prevent erroneous access, nor support interchangeability.
+
+Abstract types prevent erroneous access and support interchangeability,
+but they do not serialize trivially, nor can they be pattern-matched.
+
+(Abstract types are also traditionally desirable because they allow
+optimizations, but since performance is a non-goal of Robin, we won't
+discuss that here.)
+
+A solution is to ensure every abstract type has two operations, `to-repr`
+and `from-repr`, which convert the abstract value into a concrete
+representation and vice-versa.  `to-repr` should be deterministic; for all
+values _v_ of some abstract type _t_, all implementations of _t_ should
+produce the same value for `to-repr` _v_.  For example, an abstract type
+of dictionaries might have as its representation a sorted, non-redundant
+alist.
+
+This permits serialization, pattern-matching, equality testing, etc.,
+simply by (implicitly) calling `to-repr` on the value first.
+
+These two functions should be round-trippable, in that for all _v_,
+`(from-repr (to-repr v))` = v.  Some information may be lost, but
+such information should not be critical information (e.g. caching most
+recently used values for the sake of performance, etc.)
+
+You can still try to perform erroneous access by converting the abstract
+value to a concrete representation, mucking with the representation, then
+converting it back to the abstract value.  However, the representation
+should be defined at the level of properties of the abstraction, and trying
+to convert it to an abstract value should raise an exception if those
+properties do not hold (i.e. if the concrete value is "corrupt".)
+
+We could treat macro values as abstract values.  The representation of a
+macro value is its definition, plus any values that might be closed over in
+it (represented as a `let` wrapping the definition.)  But there is one
+desired property that does not hold -- the representation of a macro is not
+deterministic; there are an infinite number of equivalent representations of
+any macro, and no effective procedure to select the "simplest" one (or any
+other way to effectively order possible representations.)
+
+We could treat pids as abstract values.  One might hope to do information
+hiding with pids; you should be unable to send a message to a process unless
+you got its pid from it or its parent (perhaps indirectly).  However,
+`from-repr` lets you make up pids "from scratch" (from arbitrary concrete
+representations) and even if the pid structures contains an obscure
+fingerprint or the like, you might accidentally hit upon an existing pid.
+
+However, maybe that is not so bad; we could call it the "how did you get
+this phone number" problem.  Even if pids are abstract, a process can't
+really rely on its parent not somehow sharing its pid with some process it
+knows nothing about, barring some really involved proofs.  And, even with an
+abstract pid, you can't guarantee e.g. that sending it a message has some
+meaning; the process might have died, maybe long enough ago that the pid was
+recycled and a new process lives there now.  (Though, of course, we should
+take measures to reduce the chances of all these things.)
+
+Can we implement abstract values as functions?  Take a queue for instance:
+
+    (bind q (queue:empty)
+      (((q enqueue 33) enqueue 66) deqpeek))
+
+...should evaluate to 33.  This is pretty good.  What would the
+implementation look like?
+
+    (bind empty
+      (bind contents ()
+        (macro (self args env)
+          ; if args#0 is 'enqueue', return a macro like 'self'
+          ; closed over (pair args#1 contents)
+          ; else if args#0 is 'deqpeek', return (list:last contents)
+        )
+
+But it looks like this might involve mucking with the closed-over
+environment of the macro -- which we could make possible.  But I'm not
+sure we want to.  Anyway, this macro would also need to implement the
+operations `(q to-repr)` and `(q from-repr (list 1 2 3))`.  The latter
+is actually a "class method" and doesn't need to be on an existing
+instance; but that is its own, fairly involved design decision.
+
+Also, Robin potentially has the ability to use different implementations
+(representations) of the same abstract data type in different modules,
+independent of the program: the module configuration file could point
+one module to an alist-backed dictionary data type, and another module
+at a hash-table-backed dictionary data type.  Those two modules ought to
+still be able to pass objects of the dictionary data type back and forth
+to each other.
+
+#### Should environments be abstract data types?
+
+Decision: Currently they aren't, but they should be.
+
+Currently, all macros in the standard modules accept and return alist
+representations of environments.  But there are mismatches between this
+representation, and what environments actually support.  There can be
+multiple entries for the same key in an alist, and alists expose an order
+to their entries, neither of which is a necessity of environments.  There
+are potentially many ways to represent an environment.  So, environments
+should be encapsulated in an abstract data type that supports the
+operations `lookup` and `extend`.
+
+#### Should strings be abstract data types?
+
+Decision: Again, currently they aren't, but they should be.
+
+Again, there are multiple possible ways to represent a string: a naive
+list of integers (representing characters) suffices for many applications,
+but other applications may benefit from using a "rope" representation, or
+some other representation.
+
+#### Should there be a distinction between processes and devices?
+
+Decision: Yes.
+
+It's tempting to unify the two, and say that there are only devices,
+and that every concurrent process you spawn is a device of some sort.
+
+But devices represent resources beyond just cycles and memory, while
+you can have a "compute" process which just uses cycles and memory.
+It doesn't need to be acquired, or released, or registered for access
+by some other, anonymous program.
+
+#### Should the `random` facility be a device?
+
+Decision: No.
+
+Following the decision immediately above, and the decision to have
+fine-grained modules -- the `random` module itself is simply doing
+computation (generating ever-more pseudo-random numbers from a seed.)
+*However*, it may be seeded with a source of entropy from the system --
+which implies that there should, indeed, be an `entropy` device.
+
+But considering naming conventions -- possibly, for the same reason,
+the `random` module should be called `pseudo-random` or similar.  And
+`entropy` might likewise be better named `random`.  Not sure.
+
+#### Should all messaging transactions be synchronous?
+
+Decision: It's tempting, but I'm starting to think it's not practical.
+
+While the Erlang paradigm for message-passing is very simple -- just
+`Pid ! msg` and you've sent a message -- it's also very low-level.  For
+Erlang/OTP, things like `gen_server` are built on top of this, using
+sets of functions to abstract away the details.  Robin does something
+similar with `call!` and `respond!`.  This lets you write code where you
+can be reasonably sure that the other processes you are sending messages
+to are in the state that you expect.
+
+Being able to send a message to a process and not expect a reply does
+let you write potentially more efficient code; you don't have to wait for
+your message to be acknowledged.  But combining this with `call!`/`respond!`
+can lead to complex patterns of communication, where it is difficult to
+reason about whether the other processes are in the state you expect.
+(If the server process uses `respond!`, what should it do about messages
+that don't require an acknowledgement?  How sophisticated does `respond!`
+need to be?)
+
+Also, requiring synchronous communication between processes does not
+preclude asynchonous processing -- it's just that your "start doing this"
+message needs to be acknowledged by the other process, and your process
+needs to wait for that acknowledgement.  The activity itself still goes
+on independent of the current process.
+
+Also, race conditions are among the hardest bugs to detect, isolate and
+fix, and unacknowledged asynchronous messages probably lead to them (though
+not so much as shared updatable storage leads to them.)  Doing what we can
+to encourage programmers to avoid race conditions in design is probably
+called for.
+
+However... the problem is that if all messaging is synchronous, you lose
+one of the main benefits of messaging, which is (this sounds tautological)
+asynchronicity.  If you always need to wait for another process to confirm
+that it got your message, you can't do anything else in the meantime.
+
+Perhaps there is a way around this.  I'll need to come up with some
+examples and write them down here.
+
+#### Should all messaging consist of message structures (with envelopes)?
+
+Decision: Again, not sure, leaning towards yes.
+
+Again, `Pid ! Msg` where `Msg` is anything is very simple, but again, it
+is very low-level.  It is useful to have metadata about the message, like
+which process sent it, and when.  But I need to think about this more.

doc/Object_Orientation.markdown

-Object Orientation
-==================
-
-Should Robin support object-oriented programming, and if so, in what sense
-should it be "oriented" towards "objects"?  It's a tough question.  It is
-so knotty, in fact, that instead of writing about in the Design Decisions
-document, I'm going to write about it in this standalone document -- even
-if Robin's support for it turns out to be minimal or non-existent.
-
-Why would we want object-orientation?  To achive polymorphism at the value
-level.  We already have polymorphism at the module level (your configuration
-file can point a module at any given implementation of it) and the device
-level (the device manager can return any device it likes for your request,
-so long as that device supports the requested operations).
-
-Still, polymorphism at the value level would be handy.  Consider
-environments.  We don't care how they're represented, so long as they support
-a few operations -- `lookup`, `extend`, perhaps `to-alist` and `from-alist`
-or the like.
-
-It doesn't make sense to model environments as devices, because
-they have no independent existence (through time, or otherwise) from the
-code that uses them.
-
-We could simply provide multiple implementations of an `env` module, which
-use different representations, but all support the given operations.
-However, this does not buy us any data hiding.
-
-So let's consider what an object system in Robin could look like, and how
-it could be implemented.
-
-Approach
---------
-
-I would like to see the style of object-orientation to be prototype-based
-(with "manual" inheritance; if your object doesn't understand a method, it's
-up to it to pass that request on to some other object) and duck-typing based
-(no explicit classes; if an object supports some set of methods that a duck
-supports, then it's a duck, or at least you can treat it like one.)
-
-Should Robin be "purely" object-oriented -- that is, should *everything* be
-an object, or should there be some things which are not objects?
-
-I haven't decided.  If it is "pure", it will surely make the fundamental
-semantics more complex, because the base types will need to support some
-basic methods "out of the box".
-
-But the reason the core should be simple is to make a rigorous definition
-easier, and if we have a rigorous definition, and it's still not too hard to
-make a rigorous definition, maybe the extra complexity is worth it.
-
-If it's not purely object-oriented,
-
-* There might be some values I can't treat as objects.  Oh noes.
-* Different groups of developers might make up their own incompatible object
-  systems (think: Lua.)
-
-The second problem could be mitigated by providing an object system in the
-standard library -- you don't *have* to use it, but its presence would
-encourage developers to build their objects in a compatible way.  (Kind of
-like Perl; there, it's a convention, but a pretty strong convention.)
-
-Implementation
---------------
-
-Objects, and their methods, can be implemented (almost) using macros.  An
-object is a macro which expects its first argument to be a literal symbol
-which is the method name.  The rest of the arguments are the argument to
-the method.  The return value is a (possibly) modified version of the object,
-or some other value, or possibly even a pair of these two things.
-
-I say "almost" because I think we need dynamic binding to do it purely as a
-macro, or at least it would be really, really helpful.  Consider:
-
-    (bind val 0
-      (macro (self args env)
-        (let ((method (head args)) (args (tail args))
-          (choose
-            ((equal? method (literal inc))
-              (bind val (+ 1 val) self))
-            ((equal? method (literal dec))
-              (bind val (- 1 val) self))
-            ((equal? method (literal what))
-              val)
-            (else
-              (super method args))))))
-
-How would this look?
---------------------
-
-For example, lists:
-
-    (LIST head) -> VALUE
-    (LIST tail) -> LIST
-    (LIST cons VALUE) -> LIST
-
-We don't have a `List` class (and we don't want one either.)  Luckily, we
-have a built-in prototypical object for lists, `()`, so we can say...
-
-    (() cons 5)
-
-...but this actually makes the list `(5)`, i.e. it's in an unusual order:
-
-    ((() cons 5) cons 7)
-
-That one makes the list `(7 5)`.
-
-Things have a Smalltalk-y flavour after doing this.
-
-Of course, those are only the methods that lists implement.  A list,
-ultimately, should conform to a collection interface that other collections
-conform to, which supports things like `fold`, `find`, `size`, and whatnot.
-Basic lists should probably not support these out-of-the-box (the core would
-be too complicated.)  But, it should be possible, maybe, to wrap a list in a
-list-container, like
-
-    (list-container ((() cons 5) cons 7))
-
-If Robin is purely object-oriented, where do the non-instance methods live?
-Do we have class objects (metaclass etc a la Smalltalk?)  I'd rather not
-(duck-prototyping only.)  So that suggests we either put all these methods
-on some object (effectively a class object, in some aspects, perhaps), or
-retain plain macros (if objects are built from them then they're still
-available for that purpose), or something else...
-
-
-
+Object Orientation
+==================
+
+Should Robin support object-oriented programming, and if so, in what sense
+should it be "oriented" towards "objects"?  It's a tough question.  It is
+so knotty, in fact, that instead of writing about in the Design Decisions
+document, I'm going to write about it in this standalone document -- even
+if Robin's support for it turns out to be minimal or non-existent.
+
+Why would we want object-orientation?  To achive polymorphism at the value
+level.  We already have polymorphism at the module level (your configuration
+file can point a module at any given implementation of it) and the device
+level (the device manager can return any device it likes for your request,
+so long as that device supports the requested operations).
+
+Still, polymorphism at the value level would be handy.  Consider
+environments.  We don't care how they're represented, so long as they support
+a few operations -- `lookup`, `extend`, perhaps `to-alist` and `from-alist`
+or the like.
+
+It doesn't make sense to model environments as devices, because
+they have no independent existence (through time, or otherwise) from the
+code that uses them.
+
+We could simply provide multiple implementations of an `env` module, which
+use different representations, but all support the given operations.
+However, this does not buy us any data hiding.
+
+So let's consider what an object system in Robin could look like, and how
+it could be implemented.
+
+Approach
+--------
+
+I would like to see the style of object-orientation to be prototype-based
+(with "manual" inheritance; if your object doesn't understand a method, it's
+up to it to pass that request on to some other object) and duck-typing based
+(no explicit classes; if an object supports some set of methods that a duck
+supports, then it's a duck, or at least you can treat it like one.)
+
+Should Robin be "purely" object-oriented -- that is, should *everything* be
+an object, or should there be some things which are not objects?
+
+I haven't decided.  If it is "pure", it will surely make the fundamental
+semantics more complex, because the base types will need to support some
+basic methods "out of the box".
+
+But the reason the core should be simple is to make a rigorous definition
+easier, and if we have a rigorous definition, and it's still not too hard to
+make a rigorous definition, maybe the extra complexity is worth it.
+
+If it's not purely object-oriented,
+
+* There might be some values I can't treat as objects.  Oh noes.
+* Different groups of developers might make up their own incompatible object
+  systems (think: Lua.)
+
+The second problem could be mitigated by providing an object system in the
+standard library -- you don't *have* to use it, but its presence would
+encourage developers to build their objects in a compatible way.  (Kind of
+like Perl; there, it's a convention, but a pretty strong convention.)
+
+Implementation
+--------------
+
+Objects, and their methods, can be implemented (almost) using macros.  An
+object is a macro which expects its first argument to be a literal symbol
+which is the method name.  The rest of the arguments are the argument to
+the method.  The return value is a (possibly) modified version of the object,
+or some other value, or possibly even a pair of these two things.
+
+I say "almost" because I think we need dynamic binding to do it purely as a
+macro, or at least it would be really, really helpful.  Consider:
+
+    (bind val 0
+      (macro (self args env)
+        (let ((method (head args)) (args (tail args))
+          (choose
+            ((equal? method (literal inc))
+              (bind val (+ 1 val) self))
+            ((equal? method (literal dec))
+              (bind val (- 1 val) self))
+            ((equal? method (literal what))
+              val)
+            (else
+              (super method args))))))
+
+How would this look?
+--------------------
+
+For example, lists:
+
+    (LIST head) -> VALUE
+    (LIST tail) -> LIST
+    (LIST cons VALUE) -> LIST
+
+We don't have a `List` class (and we don't want one either.)  Luckily, we
+have a built-in prototypical object for lists, `()`, so we can say...
+
+    (() cons 5)
+
+...but this actually makes the list `(5)`, i.e. it's in an unusual order:
+
+    ((() cons 5) cons 7)
+
+That one makes the list `(7 5)`.
+
+Things have a Smalltalk-y flavour after doing this.
+
+Of course, those are only the methods that lists implement.  A list,
+ultimately, should conform to a collection interface that other collections
+conform to, which supports things like `fold`, `find`, `size`, and whatnot.
+Basic lists should probably not support these out-of-the-box (the core would
+be too complicated.)  But, it should be possible, maybe, to wrap a list in a
+list-container, like
+
+    (list-container ((() cons 5) cons 7))
+
+If Robin is purely object-oriented, where do the non-instance methods live?
+Do we have class objects (metaclass etc a la Smalltalk?)  I'd rather not
+(duck-prototyping only.)  So that suggests we either put all these methods
+on some object (effectively a class object, in some aspects, perhaps), or
+retain plain macros (if objects are built from them then they're still
+available for that purpose), or something else...
+
+
+

doc/Practical_Matters.markdown

-Practical Matters
-=================
-
-This document is a collection of notes I've made over the years about the
-practical matters of production programming languages -- usually stemming
-from being irked by some existing programming language's lack of adequate
-(in my opinion) support for them.  As such, these thoughts may be overblown
-and sophistry-laden, and not even things I necessarily want to see in Robin.
-But it is nice to have a central place to put them.
-
-Fundamental Abstractions
-------------------------
-
-The following facilities should be either built-in to the language, or part
-of the standard (highly standardized) libraries:
-
-* Tracing.  Ideally, the programmer should be able to easily browse all the
-  relevant reduction steps, and the relevant data being manipulated therein,
-  in the part of the program's execution that interests them.  In addition,
-  this should be something that can be enabled without polluting the source
-  code (overmuch).
-  
-  This could be done, and fairly well, with techniques from aspect-oriented
-  programming.  The rules to describe what to trace (or to highlight in a
-  full trace) could be specified in what amounts to a configuration file,
-  and thus be an implementation issue rather than a language issue.
-  
-  Unfortunately, this ideal is hard to achieve, so the system should also
-  support...
-
-* Logging.  Logging is basically an ad-hoc way to explicitly achieve
-  selective tracing: the programmer knows what points in the program, and
-  what data, are of interest to them, and outputs that data to the log at
-  those points.
-
-  Whether this is "debug logging" during development, or to support post-
-  mortem analysis of issues in production, it amounts to the same thing:
-  debugging, just on different time scales.
-  
-  The use of a "log level" is mostly just a way to filter the trace built
-  up in the log files.  This is not necessarily a bad idea, but it should
-  probably not be linear; information should be logged based on the reason
-  that it is being logged, probably in the form of some sort of "tag", and
-  filterable on that (whether at the time the log is being recorded, or
-  being read.)
-  
-  In Robin, logging should not count as a side-effect.
-
-  The logging function itself should have some properties:
-
-  - Should not have side-effects (for example from evaluating its arguments),
-    so that if it is not executed (because we are not interested in that
-    part of the execution trace) the behaviour of the program is not changed.
-
-  - In fact, should ensure that its arguments have no side-effects, and
-    ideally, be total, with no chance of hanging or crashing.
-
-  - Should pretty-print the relevant values, include the type and other
-    metadata of the values, and put clearly visible delimeters around the
-    values so printed.
-
-  - Should include the source filename and line number.
-
-  - Should not be overridable (shadowed?  not sure what I meant here.)
-
-* History.  This is more relevant in a language with mutable values, but
-  as part of tracing, it is useful to know the history of mutations of a
-  value.  With immutable values, it would be useful to be able to view
-  all the reductions which fed into the computation of the value at a
-  point.  Either way, however, this is expensive, so should be specified
-  selectively.  Again, an external, aspect-like configuration language
-  for specifying which values to watch makes this an implementation issue.
-
-* Command-line option parsing.  This should not rely on the Unix or DOS
-  idea of a command line, and it should be unified with parameter passing
-  in the language itself; calling an executable built in the language with
-  arguments `a b c` should be no different from calling a function from
-  within the language with the arguments `a b c` (probably as string values.)
-
-Reflection
-----------
-
-* First-class tracebacks.  When a program, for example, encounters an error
-  parsing an external file such as a configuration file, it should be able to
-  report the position in that file that caused the error as part of the
-  traceback, for consistency.  Java has some limited facilities for this, and
-  some Python libraries do this (Jinja2? werkzeug?) using frame hacks, but
-  a less clumsy solution would be nice.
-
-  Tracebacks are *not* a special case of logging, or an artefact of throwing
-  exceptions.  Since the traceback is basically a formatted version of the
-  current continuation, this suggests the two facilities should be unified,
-  perhaps not totally, but to a high degree.
-
-Abstractions, not Wrappers
---------------------------
-
-The basic principle here is that the existing APIs of most libraries are
-(let's be polite) less than ideal, especially when they were designed for
-some other language (such as C), and instead of blindly wrapping them in a
-new language, the designer should at least *try* to make something nicer.
-
-The abstractions should also recognize that modern computer systems are
-generally not resource-starved (or at least that truly high-level
-programming languages should not treat them that way.)
-
-This applies to very basic facilities as well as what are usually thought
-of as external libraries.  Specifically,
-
-* Date and time: We can do better than simply copycatting interfaces like
-  `strftime`.  All time data should be stored consistently, in GMT, always
-  with a time zone.
-
-* String formatting: We can do better than simply copycatting interfaces
-  like `printf`.  We can use visual formatting strings, where fixed-size
-  slots appear as fixed-sized placeholders (of the same size) in the
-  formatting string.  (See also the scathing prog21 criticism of the
-  vertical tab character.)
-
-* Line-oriented communication: We can look at line-oriented communication
-  more generally, as a form of record-oriented communication where the
-  "delimiter set" for each record is {LF, CR, CRLF}.
-
-The programmer who really wants atavistic interfaces like those mentioned
-above can always implement them as "compatibility modules" if they wish.
-
-Seperation from the Implementation
-----------------------------------
-
-This is just a repeat of the above section in slightly different terms.
-
-A language should avoid tying any language construct (e.g. imports,
-include files) to the file system or the operating system.  Instead,
-have mappings between e.g. module names and where they live in the file
-system, and between our model of a running computer and a real OS.
-These mapping could be  specified in configuration files which are
-in the domain of the implementation and outside the domain of the
-language, i.e. they never appear in programs.
-
-Standard modules supplied with the language should expose *models* of
-commonplace artefacts out in the world, for example operating systems.
-The models are similar to the artefacts, in order that the burden of
-implementing an interface from the model to any given artefact is not
-too great.  However, the models are *not* the artefacts.  Programs
-should be written to the model, not to the artefact.
-
-People who construct bindings to the language should be encouraged
-(only because they can't effectively be required) to create models
-more abstract than the libraries that they are binding.
-
-Insofar as possible, we can have a compiler optimize things so that they
-match the underlying architecture.  The language should allows and even
-encourage definitions in the most general sense; special cases are to be
-detected and optimized when they occur, instead of instituting those
-special cases into the language itself.
-
-Another aspect of this point of philosophy is that it should be possible
-to specify and change the performance characteristics of the program
-(but ideally not its behaviour) from outside the program, using
-configuration files.
-
-This counts as a practical matter because maintaining code which is
-cluttered with implementation-specific artefacts is burdensome.
-
-Serialization
--------------
-
-(This section needs to be rewritten)
-
-- All primitive values must be serializable
-- All primitive values must be round-trippable
-- All primitive values must thus have an order to them (like Ruby 1.9's
-  hashes) because in this world of representations, orderless things don't
-  really exist
-- When building user-defined values from primitive values it must be
-  easy to retain these serialization properties in the composite value
-- This is actually fairly agnostic of the particular serialization format
-  (yaml, xml, binary, etc)
-- S-expressions are trivially serializable, except for functions
-
-Formatting
-----------
-
-Closely related to serialization.
-
-Many languages support a "standard" operation to convert an arbitrary value to
-a string.  Some even have two (e.g. Python's `str` and `repr`).
-
-But in reality, there are any number of ways to convert a value to a string.
-Why should the string representation of 16 necessarily be `"16"` -- why not
-`"0xf"` or `"XVI"`?  `"16"` is fine, but it should be explicitly noted to be
-the default for the reason that it's the most convenient for the audience of
-humans who use the decimal Arabic notation when dealing with numbers.
-
-How can we support both a reasonable (and possibly configurable) default
-formatting, as well as any number of other ways to format values which would
-be more appropriate in different contexts?
-
-Can we pass a "style" argument to the string-conversion function?
-
-Should we establish a "design pattern" for writing formatting functions, and
-provide support for implementing such patterns?
-
-(Also, `format` is probably a better name for this function than `str`.)
-
-Multiple Environments
----------------------
-
-(This section needs to be rewritten)
-
-- Lots of software runs in multiple environments - "development", "qa",
-  "production"
-- Inherently support that idea
-
-Assertions
-----------
-
-(This section needs to be rewritten)
-
-- Software engineering is more about defining invariants than writing code.
-- An "assert" command which produces details errors in development, but only
-  logs warnings in production environments
-- Very lightweight so that programmers use it without thinking
-  (Python's `self.assertEqual()` is *not* lightweight)
-  (Erlang's `A = {foo,B}` IS lightweight)
-- So a conditional, by itself, is an assertion. (?)
-
-Interfaces
-----------
-
-(This section needs to be rewritten)
-
-One way or another, it should be possible to discover (programmatically,
-through reflection of some sort) the set of operations that a value supports --
-its interface.  Each operation has a name and a signature of some sort.
-
-Collections are interfaces.
-
-Some parts of an interface might be "private".  This -- information hiding --
-is obviously a somewhat complex topic.  The obvious bit is that information
-hiding is useful to prevent unintended changes to program state, but it also
-hinders debugging and testing.
-
-Usability
----------
-
-Memorization is not a good thing to make programmers do.  This can be
-addressed by either copying things from an existing language that the
-programmer base can be expected to already have memorized, or by providing
-a more orthogonal set of things which maps to the culture which programmers,
-as people, already live in.  (For example, few people in the Western world
-do not know that `&` means "and".)
-
-Non-alphabetic symbols should, idealy, have the same meaning regardless of
-the context they're used in -- in other words, the language should avoid
-using the same symbol for different purposes in different contexts.
-
-(Lots of languages are lacking here.  In C, `*` is both multiplication and
-dereferencing. In Python, `.` is both object attribute access and package
-hierarchy -- although packages are, at least, kind of like objects.  In Lua,
-`=` is both assignment and key value association.)
+Practical Matters
+=================
+
+This document is a collection of notes I've made over the years about the
+practical matters of production programming languages -- usually stemming
+from being irked by some existing programming language's lack of adequate
+(in my opinion) support for them.  As such, these thoughts may be overblown
+and sophistry-laden, and not even things I necessarily want to see in Robin.
+But it is nice to have a central place to put them.
+
+Fundamental Abstractions
+------------------------
+
+The following facilities should be either built-in to the language, or part
+of the standard (highly standardized) libraries:
+
+* Tracing.  Ideally, the programmer should be able to easily browse all the
+  relevant reduction steps, and the relevant data being manipulated therein,
+  in the part of the program's execution that interests them.  In addition,
+  this should be something that can be enabled without polluting the source
+  code (overmuch).
+  
+  This could be done, and fairly well, with techniques from aspect-oriented
+  programming.  The rules to describe what to trace (or to highlight in a
+  full trace) could be specified in what amounts to a configuration file,
+  and thus be an implementation issue rather than a language issue.
+  
+  Unfortunately, this ideal is hard to achieve, so the system should also
+  support...
+
+* Logging.  Logging is basically an ad-hoc way to explicitly achieve
+  selective tracing: the programmer knows what points in the program, and
+  what data, are of interest to them, and outputs that data to the log at
+  those points.
+
+  Whether this is "debug logging" during development, or to support post-
+  mortem analysis of issues in production, it amounts to the same thing:
+  debugging, just on different time scales.
+  
+  The use of a "log level" is mostly just a way to filter the trace built
+  up in the log files.  This is not necessarily a bad idea, but it should
+  probably not be linear; information should be logged based on the reason
+  that it is being logged, probably in the form of some sort of "tag", and
+  filterable on that (whether at the time the log is being recorded, or
+  being read.)
+  
+  In Robin, logging should not count as a side-effect.
+
+  The logging function itself should have some properties:
+
+  - Should not have side-effects (for example from evaluating its arguments),
+    so that if it is not executed (because we are not interested in that
+    part of the execution trace) the behaviour of the program is not changed.
+
+  - In fact, should ensure that its arguments have no side-effects, and
+    ideally, be total, with no chance of hanging or crashing.
+
+  - Should pretty-print the relevant values, include the type and other
+    metadata of the values, and put clearly visible delimeters around the
+    values so printed.
+
+  - Should include the source filename and line number.
+
+  - Should not be overridable (shadowed?  not sure what I meant here.)
+
+* History.  This is more relevant in a language with mutable values, but
+  as part of tracing, it is useful to know the history of mutations of a
+  value.  With immutable values, it would be useful to be able to view
+  all the reductions which fed into the computation of the value at a
+  point.  Either way, however, this is expensive, so should be specified
+  selectively.  Again, an external, aspect-like configuration language
+  for specifying which values to watch makes this an implementation issue.
+
+* Command-line option parsing.  This should not rely on the Unix or DOS
+  idea of a command line, and it should be unified with parameter passing
+  in the language itself; calling an executable built in the language with
+  arguments `a b c` should be no different from calling a function from
+  within the language with the arguments `a b c` (probably as string values.)
+
+Reflection
+----------
+
+* First-class tracebacks.  When a program, for example, encounters an error
+  parsing an external file such as a configuration file, it should be able to
+  report the position in that file that caused the error as part of the
+  traceback, for consistency.  Java has some limited facilities for this, and
+  some Python libraries do this (Jinja2? werkzeug?) using frame hacks, but
+  a less clumsy solution would be nice.
+
+  Tracebacks are *not* a special case of logging, or an artefact of throwing
+  exceptions.  Since the traceback is basically a formatted version of the
+  current continuation, this suggests the two facilities should be unified,
+  perhaps not totally, but to a high degree.
+
+Abstractions, not Wrappers
+--------------------------
+
+The basic principle here is that the existing APIs of most libraries are
+(let's be polite) less than ideal, especially when they were designed for
+some other language (such as C), and instead of blindly wrapping them in a
+new language, the designer should at least *try* to make something nicer.
+
+The abstractions should also recognize that modern computer systems are
+generally not resource-starved (or at least that truly high-level
+programming languages should not treat them that way.)
+
+This applies to very basic facilities as well as what are usually thought
+of as external libraries.  Specifically,
+
+* Date and time: We can do better than simply copycatting interfaces like
+  `strftime`.  All time data should be stored consistently, in GMT, always
+  with a time zone.
+
+* String formatting: We can do better than simply copycatting interfaces
+  like `printf`.  We can use visual formatting strings, where fixed-size
+  slots appear as fixed-sized placeholders (of the same size) in the
+  formatting string.  (See also the scathing prog21 criticism of the
+  vertical tab character.)
+
+* Line-oriented communication: We can look at line-oriented communication
+  more generally, as a form of record-oriented communication where the
+  "delimiter set" for each record is {LF, CR, CRLF}.
+
+The programmer who really wants atavistic interfaces like those mentioned
+above can always implement them as "compatibility modules" if they wish.
+
+Seperation from the Implementation
+----------------------------------
+
+This is just a repeat of the above section in slightly different terms.
+
+A language should avoid tying any language construct (e.g. imports,
+include files) to the file system or the operating system.  Instead,
+have mappings between e.g. module names and where they live in the file
+system, and between our model of a running computer and a real OS.
+These mapping could be  specified in configuration files which are
+in the domain of the implementation and outside the domain of the
+language, i.e. they never appear in programs.
+
+Standard modules supplied with the language should expose *models* of
+commonplace artefacts out in the world, for example operating systems.
+The models are similar to the artefacts, in order that the burden of
+implementing an interface from the model to any given artefact is not
+too great.  However, the models are *not* the artefacts.  Programs
+should be written to the model, not to the artefact.
+
+People who construct bindings to the language should be encouraged
+(only because they can't effectively be required) to create models
+more abstract than the libraries that they are binding.
+
+Insofar as possible, we can have a compiler optimize things so that they
+match the underlying architecture.  The language should allows and even
+encourage definitions in the most general sense; special cases are to be
+detected and optimized when they occur, instead of instituting those
+special cases into the language itself.
+
+Another aspect of this point of philosophy is that it should be possible
+to specify and change the performance characteristics of the program
+(but ideally not its behaviour) from outside the program, using
+configuration files.
+
+This counts as a practical matter because maintaining code which is
+cluttered with implementation-specific artefacts is burdensome.
+
+Serialization
+-------------
+
+(This section needs to be rewritten)
+
+- All primitive values must be serializable
+- All primitive values must be round-trippable
+- All primitive values must thus have an order to them (like Ruby 1.9's
+  hashes) because in this world of representations, orderless things don't
+  really exist
+- When building user-defined values from primitive values it must be
+  easy to retain these serialization properties in the composite value
+- This is actually fairly agnostic of the particular serialization format
+  (yaml, xml, binary, etc)
+- S-expressions are trivially serializable, except for functions
+
+Formatting
+----------
+
+Closely related to serialization.
+
+Many languages support a "standard" operation to convert an arbitrary value to
+a string.  Some even have two (e.g. Python's `str` and `repr`).
+
+But in reality, there are any number of ways to convert a value to a string.
+Why should the string representation of 16 necessarily be `"16"` -- why not
+`"0xf"` or `"XVI"`?  `"16"` is fine, but it should be explicitly noted to be
+the default for the reason that it's the most convenient for the audience of
+humans who use the decimal Arabic notation when dealing with numbers.
+
+How can we support both a reasonable (and possibly configurable) default
+formatting, as well as any number of other ways to format values which would
+be more appropriate in different contexts?
+
+Can we pass a "style" argument to the string-conversion function?
+
+Should we establish a "design pattern" for writing formatting functions, and
+provide support for implementing such patterns?
+
+(Also, `format` is probably a better name for this function than `str`.)
+
+Multiple Environments
+---------------------
+
+(This section needs to be rewritten)
+
+- Lots of software runs in multiple environments - "development", "qa",
+  "production"
+- Inherently support that idea
+
+Assertions
+----------
+
+(This section needs to be rewritten)
+
+- Software engineering is more about defining invariants than writing code.
+- An "assert" command which produces details errors in development, but only
+  logs warnings in production environments
+- Very lightweight so that programmers use it without thinking
+  (Python's `self.assertEqual()` is *not* lightweight)
+  (Erlang's `A = {foo,B}` IS lightweight)
+- So a conditional, by itself, is an assertion. (?)
+
+Interfaces
+----------
+
+(This section needs to be rewritten)
+
+One way or another, it should be possible to discover (programmatically,
+through reflection of some sort) the set of operations that a value supports --
+its interface.  Each operation has a name and a signature of some sort.
+
+Collections are interfaces.
+
+Some parts of an interface might be "private".  This -- information hiding --
+is obviously a somewhat complex topic.  The obvious bit is that information
+hiding is useful to prevent unintended changes to program state, but it also
+hinders debugging and testing.
+
+Usability
+---------
+
+Memorization is not a good thing to make programmers do.  This can be
+addressed by either copying things from an existing language that the
+programmer base can be expected to already have memorized, or by providing
+a more orthogonal set of things which maps to the culture which programmers,
+as people, already live in.  (For example, few people in the Western world
+do not know that `&` means "and".)
+
+Non-alphabetic symbols should, idealy, have the same meaning regardless of
+the context they're used in -- in other words, the language should avoid
+using the same symbol for different purposes in different contexts.
+
+(Lots of languages are lacking here.  In C, `*` is both multiplication and
+dereferencing. In Python, `.` is both object attribute access and package
+hierarchy -- although packages are, at least, kind of like objects.  In Lua,
+`=` is both assignment and key value association.)

doc/module/Bind-Args.markdown

File contents unchanged.

doc/module/Device.markdown

File contents unchanged.

doc/module/Metadata.markdown

File contents unchanged.