Rough draft for Tunes ideas.
Draft,v 1.3 1998/12/30 17:10:20 fare Exp


   Note that rule-based functions with first class patterns
and first-class contexts
is a richer paradigm of
which the most powerful OO programming is but a strict subset,
where binding and dispatching were not made orthogonal:
* Dylan-style OO is trivially implemented where pattern rewrite rules
are methods, and the function consisting in trying those patterns
is the generic function.
[* Dylan forces us to use coarse-grained statical files/modules,
whereas we could have fine-grained modules. ???]
* The module and generic function system in Dylan is dynamic,
with possibility of "closing" completely a module;
by making pattern-matching and binding orthogonal,
we allow any kind of partial closures in generic functions.

   We should encourage statical restrictions to contexts
as the/a usual way of using them, so that semantics are clearer.
A context would thus be a list of binding a finite part of which
is exported.

------>8------>8------>8------>8------>8------>8------>8------>8------>8------

   We need some clean semantics for Migration:
how to simply express that an object stays the "same",
while changing its context ?
What is the "type" of migrating transition operations ?
   For instance, let's assume we have an object
G that needs a video object V. What is the type of the operation
that takes the (G linked with V0) cached value, and
returns the (G linked to V1) cached value ?
or the (G with V abstracted) value ?
Note that the difficulty lies in the fact that we want the "same" G
to be taken and returned.
   A solution means that we annotate types with side-effective information,
that gives a pattern of what kind of of side-effects are possible.
Side-effects consists in reading, writing, cloning or destroying
information provided by some resource.
   So a function f(a[i]) := b[j],
of simplified type (by neglecting side effects) A[I]->B[J],
may take into account that resources r[k] of resource types R[K]
are the only one involved, such that r[k] was input from parameter a[i[k]]
and output to parameter b[j[k]]. That resource be created ex-nihilo or
annihilated back, can be considered as using an implicit parameter,
that could be made explicit (and vice-versa) by means of higher-order
operations.
   Note that I quite prefer the terms "resource" and "resource type"
to the quite confusing "unique object" and "unique type".
   Positive and negative expressivity with respect to side-effects
is as important as positive and negative expressivity with respect to
structural types.

   What are positive and negative expressivity exactly ?
I feel that the distinction is too unprecise,
as there may be an infinite hierarchy of expressiveness,
where so-called "positive expressivity" would be the lowest layer,
and "negative" one all the above layers,
or perhaps just the highest if exists.

   Now, if M
mappings betwe
mappings

------>8------>8------>8------>8------>8------>8------>8------>8------>8------

And what about dealing with multiple instances of objects when recovering
from a crash ? Can/must we avoid to express recovery in terms of normal
Tunes operations ? If not, does the meta- nature of the operations protect
us easily from inconsistencies ?


------>8------>8------>8------>8------>8------>8------>8------>8------>8------
we have a universe of raw objects,
and a sub-universe of interfaces to objects, "views" of objects

we try to attach every object to a one context-dependent view,
so we can save it



Parser:
* a generic tokenizer,
  with list of special symbols with special actions,
  
* longest match 

symtab:
 * we have parse contexts as symbol->action associations
  a symbol is just
 * in the initial implementation, contexts are implemented as
  a linked list of association functions to try
  counted association arrays.
 * as a standard function, one that takes
 * as a standard action, pushing a computed
  -
  -
 .....

------>8------>8------>8------>8------>8------>8------>8------>8------>8------
* advantages of ret-based:
  - the ret stack is the same as the code-cache stack
  - C<->LLL routines used quite easily !
  - bytecode routines are expanded in one round,
   allowing efficient hardware caching of translation addresses
  - loops are expanded at runtime by
   optimizing cache use
* disadvantages:
  - wastes memory as compared to traditional threading methods
  - uses cache space
  - special care must be taken for register usage:
   all imported modules must be acknowledge the calling conventions,
   and filter modules must be used to access external modules that
   don't (an automatic filter generator may be used).


------>8------>8------>8------>8------>8------>8------>8------>8------>8------

As for graphic drivers, when it comes to it, the contact to get is GGI
(see dow/www/OSes.html#GGI)

Andreas Beck (becka@hp.rz.uni-duesseldorf.de),
author of GGI, the "Generic Graphics Interface",
which should ultimately replace all the chipset
code and converge X, svgalib and SVGATextMode,
so they all use the SAME graphics drivers,
with less cooperation problems

------>8------>8------>8------>8------>8------>8------>8------>8------>8------
Resource management:

* it is easy to create logically separate pools, that will just consist
 in a resource counter, but share the actual pool with a higher GC-ed
 resource space (of equal protection constraints). The counter is maintained
 for out-of-resource stuff, as well as a max counter. The max counter
 is completely reserved from "available resource" in the higher pool,
 but can be decreased if the higher pool needs it so.

* By default, some minimal pools of resource are permanently left
 and maintained "ready" for root debugging interaction if needs be. E.g.

* The implementation: some pools are "current", and have individually
 and independently updated usage statistics.
* at some events, the father pools are updated
* such an event is when a father pool is asked for resource, which notably
 happens when immediately available resource was filled, and more is needed
* another event is when there is a context switch and pools are no more
 current
* when several subpools share a pool's space, they must "preallocate"
 resource from it, and the main pool gives each requestor a space that it
 can be sure to use comfortably. When some subpool fills its space, it
 has to preallocate more resource; if the main pool runs short of resource,
 it may renegociate resource usage.
* typical data about resource usage
 [min used]
 min use expected
 current use
 [max used]
 expected max use
 preallocated resources
 [max use]

* Common

-----------------------------------------------------------------------------
Basic Operators:
* arithmetic/logic/*
* stack/structure/variable manipulation
* function call/environment abstraction
* dynamic dispatch
* lazy evaluation/force constructs

-----------------------------------------------------------------------------


The error of most languages (human or computer) is to believe that
the word is the meaning, or to make believe it, by assuming or
creating a "canonical" (priviledged static) mapping between words/sentences
and some meaning. This is an error, which is completely opposite to
the relativity of meta(n) contexts. Between words and meaning is a
dynamic, refinable mapping.


-----------------------------------------------------------------------------
scope, meaning, etc:
* until explicitly bound, every occurence of every symbol is left dynamically
bound
* in that way, things such as side-effective variables
* variables bound in a same context are supposed to be independent unless
 declared otherwise. That is, independent or not, their inter-relations
 are known.
 e.g. when declaring variables A and B in a routine, unless declared
 as sharing fields, they will be independent.

--------------------------- Copyright Protection ----------------------------
   Let us introduce a new concept in software piracy protection:
*philosophyware*. There already exists protection schemes for software based
on asking the user what word is on such page of the manual. But a manual is
easy to copy, least it is large, and large manuals cost a lot to produce,
not to talk about writing them. The solution is not to write the manual,
perhaps not even produce it, but sell some philosophical work together with
the software.
   It has many advantages: firstly, philosophy books are large enough for
manual-based testing to be valid (you can ask three times for a word among
tens of thousands of them); these books are uncommon enough so that a
pirat won't have it at home already, so that he would have to purchase a copy
(perhaps an expensive one) to run the program; if the book is too well-known,
just having an uncommon edition may make it harder for a pirat to find the
right key word/sentence; likely, even if the pirat has got an electronic
version of the book, he'll have a hard time tracking down the mapping between
the key edition and his electronic version; now, as the book already exists
independently, it costs much less to publish it together with the software,
than to write and publish a new book of same size in a special series;
Last but not least, as a side effect of using software (be it legal or
pirated), the user will happen to enrich his mind with a philosophical work !
   Please do not forget that whatever you do, you can't prevent piracy,
least you make your software so private that very few will use it, so you
won't receive feedback, won't evolve much, and will be beat by competition.
Philosophyware allows you to be sure that at least, the pirats will have
read philosophical books.



--------------------------------- Overview ----------------------------------
Zoom in.
Firstly, a novice approach

What kind of programs:
* programs w/ (possibly real-time) user management choice,
  random/experimental data, real-life interaction,
  e.g. games with player management (possibly action), luck (or otherwise
  undocumented input), and discovery, between (possibly dissymetrical) players
  or a company's internal software, with management, action, human and
  experimental input.
* How the code is executed: modularity
  every user has got his favorite input/output method:
  players have their favorite, workers use aliases, etc.

  How to extend modules: meta(n) programming,
  Meta-Games: programming robots game players
  Meta-Work: automating boring work, filling intelligently, consulting charts,
	advising, repeatedly but parametrizedly fill letters, etc.



-------------------------------- Semantics ---------------------------------



*
*
* semantics based on higher-order reflection:
   * rewriting an expression into another.
   * choosing between implicit/explicit possibilities.

   * annotating:=particular rewriting


* reflection is homogeneous if you can reflect into local contexts
 instead of having a one global reflection operator.
 Active dynamic annotations *are* this, up to isomorphism.

* annotating is difficult 

* message passing is linked to object having a state
 what are messages ? They can't be "objects" if you define
 objects as things you pass messages to; or they need be
 simpler objects. In any case message passing is not a good
 paradigm; it is issued from low-level implementation point
 of view.

* 

* Signatures, terms:
 Well-known.

* Typed signatures, typed terms:
 There is a preliminary (most often computable) theory on expressions,
 expressed in some (other)
 signature, and we consider only typed expressions.
 That is, we consider only terms that satisfy some typing, add this
 well-typed condition to all quantifiers, and .....

* Theories:

Let S0 be a typed signature.
S1 is an refinement of signature S0 iff S0 < S1, iff S0 is included in S1
Let T0 be a theory in signature S0;
a refinement theory of T0 is a theory T1 on a subsignature S1 of T1,
that contains T0
The projection of T1 on S0 is the finest theory T0 on S0 such that T1 is
a refinement of T0.


* Rewrite:
Let S1 and S2 be signatures, let 
Let T1 be a theory which refines T0. Let T2 be a theory



* 


We have object X in Theory T(X)

* overview:



-------------------------------- Vocabulary ---------------------------------

* Let's say specializer/mixer instead of optimizer.

* Humor, simplicity, ... What's the corresponding programming style ?



---------------------------------- French -----------------------------------
* Implementation is best translated in french by "realisation",
 or "mise en oeuvre".


------------------ Mid Level Implementation Annotations: --------------------

-- A --
directives:
 __code_implemented__	general directive meaning that something may be
			implemented as executable code to increase time
			performance.
 __data_implemented__	general directive meaning that something may be
			implemented as data to simplify code semantics.
 These are general directives; they do not say exactly how the object will
be implemented; they just tell the specializer to think in that direction
first.

examples:
* let x be __code_implemented__ in { 1, 2, 3 }
 It will produce three versions of the procedure where x is defined; one
for each value of x

* let J be a __data_implemented__ involution of the Multipliplicative Group
of Z/nZ{ n=256 }

* let K be a __code_implemented__ exception (* the standard way of doing it *)

Notes:
* This has to do with factoring optimizations in the case of a run-time
constant (i.e. something that remains constant long enough (=is used many,
many times with the same value) for the program to be accelerated by
folding it).
* Data implementation is implementing the object as data for another one;
that is obj0 is implemented as specialization of obj1 by data obj2.
* Code implementation is having a variable as function of the program counter,
and is useful if it doesn't lead to too much copies of specialized versions of
the code, and of course if the specialization brings good results.
* Code implementation of increasing data is useful when combined with the
code corresponding to data below the current one being discarded. Such code
must be really discarded (i.e. for all instances of the object in memory) for
this optimization to be useful. This is the case when a program initializes
itself (for example, when the OS looks for hardware features and discards
drivers that aren't susceptible of being useful, or when a drawing software
initializes sine and cosine tables or such adapted to screen resolution, etc).


directives:
- specialize_for{ (distribution of bindings) }
- optimize_for{ (distribution of bindings) }


------------------- Garbage collecting and pointer problems ------------------

* Garbage collecting is necessary when data sharing make it impossible for
user objects to determine when an object is truely unused; then the object
server must manage this issue.
* In the (general) case of multiple object servers (and particularly in a
distributed or multiple system) (by multiple, I mean multiple subsystems
share the same resources, as with DOS, Apple ][, etc emulation), a GC
occuring in one server may lead to GC occuring in all servers (this is a
BIG problem for world-wide systems...). The inter-server gc must be adapted
according to speed and delay of inter-server communication, and may vary
according to server pairs. Thus, object servers are grouped in universes
of servers having common GC; GC accross universes is done by having
bidirectional pointers.
* The system must still live, even if a garbage collecting is on.
data being initialized must allow a GC occurrence at ANY time, even
while swapping pointers in a doubly linked list, for instance. Thus,
either critical portions of code must be able to prevent interrupts, or
the GC must be aware of the system being in such code portions and act
accordingly (=exit portion, or have portion-dependent GC; heavy !).
* Reserving resources: some time-critical or system code must also go on
working even if a GC is occurring, or no more memory is available. That's
why priviledge objects may reserve memory or other resources so that in case
of need, they don't have to rely upon GC or swapping. Note that sometimes, you
need only escape unavailability, but would like to have resizability. In those
case, you may still use GC, as long as the minimal size is reserved, and that
the GC process won't disturb you. This is the case when reserving pages of
physical memory. Pausable GC, i.e. the GC ensures that memory won't be
unavailable for more than a small time is also a solution. Such a GC will
certainly be mainly done through paging so that's almost th previous solution.


------------- Isolating or merging objects (=saving, loading) ---------------

* We must define the limits of a non-elementary object (i.e. object that
includes multiple parts with pointers to each other and to external objects).

----------------- Sharing objects between multiple users --------------------

* each user can add attributes to objects. These attributes may in turn be
read or modified by other users (according to the object's sharing policy).

* The first problem is how to implement these so that reading an added
attribute is not much harder than reading a "normal" one.

* In case where a user want his personalized object to evolve with another
user's version (e.g. the system administrator's version, etc), the problem is
that the user may not want exactly the other user's version, but a modified
version; there must be a system to log changes and apply them if they do not
interfere with the user's one; if the changes interfere, there must be a
system to rule which will win (it's safer to memorize the old user's version
before the modification); in case the modification is "equal or better" to
the user's own, it must be done (e.g. if the modification was done following
a suggestion from the user).
  Sometimes, the "original" file isn't even completely trusted. Thus, not only
should a copy of a "working" version of the user-customized file be maintained,
but it something should be made to allow the user to go back to it and manually
make any modification just in case.


----------------------- Distribution and version control --------------------

* As an object spreads accross the world, multiple copies of it are created;
some (a few) of them are modified, in good or ill. To manage the multiple
versions of an object is the role of the version control part of the software.
** Wish List
- newer better versions should replace older ones
- in case the new version may have lost data important in the old one
(=bug introduced, feature destroyed, or quirk disabled), the change must be
reversible until eventually proven right by everyone concerned.
- particularly in case of automatic update, security (1) is also a major
constraint. Thus an official maintainer is necessary for each evoluting
world-wide object (see the FSF and GNU software, etc).
- Now, there are cases where different people maintain concurrent versions of
the "same" product (example: MS- or DR- DOS, GNU or lucid emacs, the great
variety of compilers for the C language or any other, etc), whether because of
financial rivalry, or the lack of some features in the other versions, or the
unavailability of the other versions (again, for financial reasons, or any
kind of net failure). There must then be a system to have the version diverge
(each isolated portion of the net may modify its version of the software),
and to merge versions again when the net has been (momentarily or permanently)
reconnected.
- Each maintainer must export release versions, and amount of change between
consecutive versions and previous major release version, frequency of change
in released code, mechanism of update (=where to find most recent code),
merging decision mechanisms, level of testing of the release (=alpha, beta,
gamma versions).

(1) Security: Errors or piracy should not allow introduction of loss of
information or abuse of system resources, or "security holes" that would
allow further errors or piracy to achieve more easily such loss or abuse.

-------------------- Persistence and Disk consistency -----------------------

* The Disk Object-System (DOS ;) must ensure maximum disk consistency:
most of the time, the disk contents must be consistent.
In the best case, this means that when the disk is not writing data (i.e.
displacing its head or waiting), the disk contents are consistent.

* Consistency means no existing object is overwritten before a new version
is made available. A strong version will not even allow overwriting by the new
version. Then, a special pointer in permanent battery backed CMOS memory will
show the main block, or it may be made more or less obvious to a recovery
utility which candidate main block is the latest consistent head for the
system.

* A special indicator in the CMOS memory indicates that file systems should be
checked if it is not present, or does not correspond to some value. On PCs,
the real-time alarm could be used as such a flag

* OTOP, a simple non-real-time write() mechanism could be used, where you
  write() the whole memory image at each checkpoint.
* a more real-time-like mechanism would require that you manually maintain
  which pages were modified (i.e. a write barrier). Separating objects into
  logical zones would make it both easier and faster to implement such
  barrier.


---------------------------- Proposed file system ---------------------------

* Files are a low-level concept. Users should not see them, unless they
really need and want to (e.g. when explicitly archiving objects on
floppies, tapes or other media).

* Files are physical representation of objects on disk. They must be
isolated (or at least isolatable) from the in-memory structure and location.

* The system will allow efficient allocation of both big or small files.
Because these are completely different phenomena, these should be
managed separately.

* It will allow several cooperative or preemptive compression schemes:
objects are annotated with information that helps determine what compression
algorithm to run on it, when, etc.

* Actually, the file system will be divided in three layers: the blocking
layer, the filing layer, the compressing layer; the implementation of these
layers will mix them somehow to increase performance, but that's a compiler's
issue (even if code is hand-compiled to a low-level language like C).

* The blocking system will divide the disk logically in cylinders; a cylinder
is divided into blocks sized according to the processor paging
in used architecture (4KB for the i386, 8KB for the sun4, etc). The remaining
of the cylinder (if the cylinder size is not divisible by the block size) is
reserved for use with out-block allocation.

* Sectors may be unavailable in a cylinder due to physical error or a
reservation by another system resource (for example partitioning compatible
with another OS). Cylinders need not have all the same size. This can be
implemented by having unavailable ghost sectors in smaller cylinders...

* The filing system divides files into blocks; If file size is not divisible
by block size (in particular if file size is non-zero and smaller than block
size), the remaining is put into a special out-block file that uses the extra
sectors together with regularly allocated blocks.

* A same file can have different compression methods and strategies.

* Compression methods include no compression, huffman compression with
precalculated or included table, lempel-ziv compression with or without
huffman compression above, etc.

* compressed file may or not contain physical <-> logical pointer conversion
tables for easier random access. Compressed file may be completely uncompressed
when actually modified, and eventually recompressed only if closed by all
modifying processes.

* Files of some length may have a measured life, change rate, expected rest of
life. Compression strategy is done according to this data. Files that are
mostly read-only and/or that haven't been modified for a long time are
automatically compressed. If the file is long and with random access (as
opposed to sequential access), it is divided in compressed blocks such with
pointers to make correspond physical and logical blocks.

* All file-system level compression may be disabled with a file local flag.
This is useful to save time when the file is already compress, and further
compression is either unuseful or harmful, and spends time needlessly.

* Logical objects components can be separately compressed, opened, etc; As the
file system manages well small objects, the saved object must just export
explicitly the separate fields as being separately compressed.

* If multiple disks are available, one may contain swap and system pointers,
the other actual data.

* a checksumed CMOS indicator may indicate (if valid) possible states at
shutdown time: stopped (ok), writing data (must return to previous data),
data written but not pointers (may discard old data)

* If a great amount of frequently changing persistent data is needed, a
special sector may be reserved in each cylinder to contain the latest version
of these data, with a pointer to the latest version in CMOS and/or the date
of the version in each. This method may prove expensive if cylinders are small.
Having one such reserved sector every n cylinders is cheaper, but requires
seek time to be reasonably smaller than demanded update cycle. Less or more
than a full sector may be reserved each time, if the file system is written
accordingly (i.e. reserve the beginning of the per-character allocation).

* Automatic disk "compression" (block grouping) can run as a back-ground
process, grouping in priority read-mostly files.


---------------------------- Naming objects ---------------------------------

* First name the universe, then have a universe-dependent name.
a 64 bits number should be sufficient for each field, and may include a
key.
 
--------------------------------- Kernel -----------------------------------
* The kernel is the smallest part of the system that can consider itself as
an object. It must then be both an object server and an object inside it.

-------------------------- miscellaneous ------------------------

- The standard object server has self-adapting capabilities: objects
dynamically divide between attributes and functions; attributes dynamically
divide into dictionary-bound and reserved attributes.
- a = x+1 <!=> a = [x+1] <=> a = [x]+1  (where [o] is current value of
variable object o)
- implementation must group mutually heavily dependent code do achieve
better instruction caching.
- A user can customize attributes: Either he uses an object-external hash
table for objects with few added attributes, or he adds a personal
user-customize attribute to any object he wants; Then, the attribute manager
will be aware to look in the hash table first, then look (recursively) for
user-customized attributes before to use standard attributes.

------------------------- implementation ------------------------

- A generic thread object has Activate and Unactivate methods. System-level
threads may in turn have sub-threads that they activate at will, etc.
A good system will never know that it is a sub-system, except by measuring
leaks in system resources usage.
- an OSL Thread saves perhaps registers, but most importantly the OSL
context. Among these, default stacks for each opened class. Among the classes,
int, addr, code, obj have reserved cells.
- non-preemptive cooperative threads may be a better implementation than
preemptive threads, as the one who accepts to give the hand away will save
only what he needs, and not all the machine state; moreover, he will leave
the machine in some kind of good (not intermediate) state such that all data
are valid (all pointer variables point to some initialized zone, etc).

-------------------------- optimization ------------------------

* optimizations may be published so that new optimized programs can benefit
 from these information.


Here are some "optimizations" (man I hate that word), that we commonly
do when writing programs, and that compilers should do too, when they
are indeed useful:

* if we have A=f(B) defined somewhere, we compute A right after B
 (so that we have less overhead loading B), instead of doing it at the
 place corresponding to where A is syntactically defined.
 Note that this can be done in combination with factoring;
 e.g. we could have A=f(B,C), with f(B,C)==(g(B)) C, so we can
 compute h=g(B) right after B is available, and let A=h(C) be managed
 by further optimizations (perhaps the same).
* Conversely, if we know B is needed only when A will be computed,
 we can defer computation of B till then.

* disconnection between physical type and logical type.
 The best ad-hoc encoding is chosen by the optimizer, so that the high-level
 logical type is not a low-level implementation description (unless stated
 otherwise).
 - An integer can be represented in weird ways. Typically, it could be
 multiplied/shifted/added by/with some constant, so as, for example,
 crossing a bound is detected by a mere carry or zero test, not needing
 a comparison.
 - Logical records can be physically divided. Elements can be spread around
 to optimize some initializations of fill holes.
 - Arrays can have holes filled in the above way; array of records can
 be factored in record of arrays and conversely.
 - Several variables can have the same address, if their lifespans are
 disjoint. A "same" (syntactical) variable can see its location change
 with time in a statically determinable way.
 - Procedures with same logical type can have different low-level
 calling conventions, for sake of optimization, while conversely,
 procedures with different logical types can share the same calling
 convention are worse, *be the same physical procedure* !
 - Particularly, arguments and local variables are reordered so as to
 optimize their pushing/popping.

* General method:
 - firstly, modify expression trees so as do diminish memory required
 for stack/heap execution, then group terms according to instruction set.

* Annotations from User/Runtime-feedback for optimization
 - branchs: an annotation can say which part of a branch is the
 most often taken. In some cases, this can be context-dependent,
 and either a medium case is chosen, or context-dependent optimization
 is done. The optimizer can optimize according to statistics given,
 either human synthetized, actual feedback, or any combination.

* Final function call:
 function calls followed by return, and non-returning functions,
 continuations, are implemented by a jump instead of a call followed
 by a return.

* A very important part of an optimizer consists in detecting
 (local) (higher-order) constants and/or rewrite/factor code so as to make it
 explicit that the constant is used, then inline it.
 The generic case is an undecidable higher-order unification problem,
 so we have to have (programmable) heuristics here.
 To begin with, we'll support simple forward and backward tracking.

* Recursivity can be rewritten in CPS, with higher-order parameters.
 Parameters detected as constants can be inlined.
 Parameters that are detected as linear can then be destructively updated.
 Parameters that are detected as non-escaping can be implemented with a stack.

* use registers to contain locally often-used constant values
* When incrementing/decrementing is cheap (particularly on CISC computers,
 and when considering memory cost), try using decrements and increments
 on the previous registers to increase their utility


--------------------------- Interactivity ------------------------

- by default, nothing is really a constant, as its type may change.
- to force a constant to be one, the user must "compile"
- being a constant is something very relative to the object that sees
you. You are constant to the object if you don't change during the object's
lifespan.

---------------- Hierarchical Constraint Logic Programming ---------------

  Contrarily to the xxx article, we don't think the constraint hierarchy
should be linear. Of course, during the process of resolving the constraints,
the system may choose a linear extension of the constraint hierarchy. But the
hierarchy is essentially "parallel"; it's a partial order, not a total one.

  Different users may be thought as independent nodes of this partial order.
Each user controls sub-constraints. User interact when their sub-constraints
add or negate each other.


---------------------------- Programming Plan ----------------------------

- Build any objective implementation
- Build I/O
- 

----------------------------- Object semantics ----------------------------


* Universes

   There are Universes U of objects.
   Among these are the two booleans True=1={empty}=Yes and False=0=empty=No.
   Any object may be compared to a boolean.
   Some objects operate on some others, and may return another object or never
return. We note f A the operation of f on A, and abusively its result.

A =(C) B  ::=	for any operator f in C,  f A = f B if one of f A, f B returns
		a boolean
A = B	  ::=	A =(U) B

   see lambda-calculus et al.
   Note that different universes may well contain each other.

* Machines

   Now, we have machines, which at any moment, consider a finite subset of
objects in a Universe U, called the state of the machine. Among these is the
dynamics of the machine. Each machine modifies its state by having the
dynamics operate on the state, which forgets some objects and/or adds new ones
that are result of operations of objects in the previous state.
   Note that a machine's state can contain sub-universes of U, and thus other
machines.
  

* Relations & Goodness

   Relations are special kinds of objects which always return a boolean.

   Considering a set of relations arbitrarily ordered, the state of a machine
is said to be good if all the relations would return true if operating on the
machine state. The goodness of a state is the subset of considered relations
such that all relations above it are verified by the state. A state is better
than another if its goodness contains that of the other.

   A good dynamics is one that given some state will stop on a better state.
Some dynamics is better than some other if it always stop on an even better
state. Note that before it stops, the machine may well consider states worse
than its starting state.

   Note that finite machines will behave well only for a finite (but
expectedly large enough) part an infinite Universe. For those machines to be
good, we must restrict the relations so that they specify the machine always
consider a good finite part of the Universe.

   An actual computer will always be such a finite machine.


-------------------------- OSL primitives ----------------------------------

  Given a universe U, we may consider objects Oi inside. In OSL, we consider
some special kind of good universes. In these there are some standard objects
according to the goodness of the universe.

  A Multiplexed Universe contains tuples; there is an operator creating tuples
(Pair), and functions (Fst) and (Snd) such that for any objects A and B,
(Fst ((Pair A) B)) = A and (Snd ((Pair A) B)) = B.

  A Set universe contains sets; sets come in two flavours: recursive sets and
enumerated sets. The first ones are computingly more usable. A recursive set
is an object always answering Yes or No.

  A Class universe (U), contains classes (which can be identified by the object
IsClass), and each class (C) is a universe whose operation and some objects are
included in (made available to) the Class universe; among these is an object
(IsMemberOf C) that answers (Yes) iff an object is in (C). An object (O) is
always considered together with a class, that can be returned by the object
(ClassOf O). Any class (C) is of the class (Class). An object can operate on
another of class (C) iff its class verifies (OperatesOn C). A Class contains

  A Named Universe is a Class Universe that contains a class (Name). An object
(ObjectNamed N) may be associated to any name (N); different names may refer to
the same object. Eventually, each object (O) comes with at least a name
(NameOf O), such that if (N) is that name, (ObjectNamed N) is (O).

  The (Sequence) Class

  A Stack Universe contains a (Stack) class with (Push), (Pop)

  A Number Universe contains numbers, with the usual operations.

  A memory universe contains for each object a si

  An interactive universe contains input streams and output streams, each of
some class.

  An auto-coding universe 

  An effective universe is contains among others effective objects. Each
effective object can be rough data or rough code. If it is rough code, it has
a calling convention. Before using an object, the machine must change its
calling convention state; therefore, there are calling convention transition
operators.


#### Code separately the __Static__ "data" universe and the __Dynamic__ "code"
#### universe.
#### There is an axiomatic (system-dependent) dynamic object "clock" with
#### infinite number of state changes, and with grain finer than anything else
#### in the system.
#### Mutable data are part of the "code" stuff. Objects that mute, approximated
#### by f(clock)-periodic objects (( f is an integer function that bounds
#### up or down the time between mutations of the object ))


------------------------------------------------------------------------------

OTOP:

- main server
 * user-based ?
	-> no internal inter-user communication :(
 * or suid root and changing uid to become client user as required ?
	-> security hole that many other people won't allow
 * or "intelligent clients" that perform user-level things ?
	-> 

 ==> To me, the final solution is neutral servers (you connect to a socket
    and/or to shared memory; in the latter case, a network client can serve
    as an intermediate to map socket calls into shm calls) with chroot
    moose rights, and intelligent clients, that do
    read/write needed global files/pipes/sockets/devices and/or serve as
    intermediate between different servers, and are able to launch a private
    server if needed (no public socket).

 ==> A client can save its objects and (re)launch another server with its
    objects in the same state as the previous one left them in. In particular,
    a priviledged client (owner/root) can

- The event driver, if required, will fork/lightfork the server into the code
 executer and the keyboard/timer/mouse/network event listener.
 See dosemu "dosipc.?" files to see how it does it. Try to change forked
 arguments to show the second task so that ps will distinguish the main server
 from the forked thing.

- 


------------------------------------------------------------------------------
VSTa:
- bad language ("C" -- yuck). That's NOT easily correctible.
- no checking semantics. No typing -> VSTa is unusable
- particularly, everything is done through more or less standard names.
but a name is but noise and dust. Thus, VSTa is very unstable. Moreover, there
seems to be no alpha-conversion (renaming) mechanism. Yuck.

------------------------------------------------------------------------------
Not Names, but signatures with properties.


------------------------------------------------------------------------------
   The good trick to use preallocated memory with a routine that calls
the dynamic allocator: patch the jump table so that it will return the
preallocated memory block, then patch back the table.
   This means that the address of the allocator won't have been linked in.

   This leads us to the concept of hooks.

   A hook is a point where a user-defined function enhances or customizes a
standard software behavior, upon some event. The difference between hooks and
just event-driven routines, is that hooks may behave as filters for such
events, and the event will not look the same before and after the hook was
executed. That's why hooks organize in an arbitrary (partial) dependence order
(mutual dependence must be resolved at this state). A common event-driven
routine is one which does not modify the event on which it depends; for
example, assuming the system timing routine is ok, hooks depending on the timer
are just event-driven routine. They are hooks if they maintain some alternate
timer visible instead of the system timer to objects below. The concept of hook
is strongly related with the concept of user; depending on what you think is
the user, something will be part of the system, or a hook, or a sub-user's
object.

------------------------------------------------------------------------------

* Language: text is enriched when read by its meaning. Ultimately, only the
meaning will remain while the exact text will be forgotten or at least much
simplified.

* Language semantics: attributes may be given to word sequences. Then when the
sequence is done again, the attributes must be retrieved again.

Example:  (polished> stone) >is< ((an> improvement) <over> (cut> stone)).
  then when we talk again about polished stone, and ask if it is an
 improvement over cut stone, the system must answer "yes". If we ask the
 system all it directly knows about polished stone, it must retreive that. Of
 course, the system may know much more things about polished stone. Firstly, a
 polished stone is a stone (unless stated otherwise in the definitions).

------------------------------------------------------------------------------

OTOP:
- it's too clumsy. First build an API with lighter threads, signal managing,
file interface, special file (blocks device, socket, pipe, tty) control,
exec'ing external commands, calling routines from external object modules,
writing such object modules. Then, work inside that. Note: AAARRRGGGH ! I
realize preemptive light threads are undoable, unless we have an interpreter !
So let's have an interpreter.
- finally, using the usual trick is ok: do not preempt, but pause regularly
and end when it's ok. -> BSD sigstack welcome.
- A way of using a very simple API is working with only stdin and stdout (or
command line redirection of these). External programs filter stdin and stdout
to multiplex i/o (e.g.: sh as stdout interpreter), a named pipe as stdin,
with feedback from the stdout sh to it...


SI:
* No "Class" attribute, just a set of standard attributes: memory manager,
priviledged aspects, other aspects, etc.
* An aspect of an object O is the interface of another object O1 such that O
is a refinement (specialization, implementation) of O1.


i386:
* Use Relocatable zone between 1GB and 3GB only. Thus pointer values between
-1GB and +1GB are constants, and can be used as integer values too !
* 

pcore:
* Use a byte compiler to a 
* use mmap. But must be able to trap faults, or checking for overflow will
have to be done. Else same as i386 (?)
* If bit to reserve, add constant odd offset to pointers and multiply
 integers by two -- then pointer/integer arithmetics is conserved !



Both cases
--------------------------------------------------------------------
HL:
Actually, a class is an aspect of the object among others. Each object is part
of infinite number of classes.

LL:
The LL class describes the implementation of the LL object.

------------
The memory is divided into pages of objects having the same sizeable LL class.
example: strings, cons cells, class-preceded objects, etc. Big objects have
their own set of pages.

The (universal but clumsy) clad LL Class has consecutive cell fields:
- annotations (linked list of attribute <-> value)

------------
Pretty printer for in-memory objects: ???

------------
event programming is near functional programming in that instead of
reading an event E from an input with side effects to compute the value of
f(E), you directly ask the calculus of f(E) without visible side effect.
Of course, f may have side effects, but that's one side-effect less.








------------------------------ Buffering --------------------------------------
A buffer is an object that allows to connect an output-driven data producing
stream to another stream.
filters the output of an object so that it can
become the input of another one (so that it transforms an output-driven
stream into an input-driven stream).

A buffer is defined by two functions:
* when to block the writer	(if he wrote too much data)
* when to block the reader	(if he not enough data is present)
  The first condition to be filled, is that "enough" data is less than
"too much" data.
  Then, the writing object closes the buffer end, there is no more blocking
for the reading object; whereas if the reading object closes its end, the
writing object may be killed because not needed any more (not the case if
writing to more than just the closing reader).
  New problems arise when


that must be compatible in one way:
- both the writer and the reader can't be blocked
- if one flushes his part, the other must be unblocked





Defaults
-


------------------------------------------------------------------------------
Memory manager:
* Generic MM

* MM Page Allocation Layer:
 getting consecutive pages of virtual memory through MMU,
 moving them, copy-on-write, etc.

- BIBOP (da lulla): BIg Bunch Of Pages.


------------------------------------------------------------------------------
Word-code indirect threading
* opcodes are 16 bit tokens.
* threading is achieved using a jump table.
* Each thread has its own jump table, according to the modules it opened.
* Jump tables are shared by pages !
* That is, each page has PAGESIZE/POINTERSIZE (1024 for the i386) entries,
 so modules allocate opcodes without breaking page boundaries.
* This gives a limit of 65536 different opcodes and (minimum) 64 opened
 modules for a program.
* sharing of code between programs is then difficult. All these hacks are too
 yucky. And most RISC computers won't have a 16-bit data size, so this will
 be slow.

------------------------------------------------------------------------------
Sometimes, doing things immediately instead of doing it partially
saves memory. i.e. linking a process entirely instead of linking each object
lazily at its time. This saves memory, unless the linking tables may be
shared or swapped off memory.

Thus, when linking an object in a module, all the module is linked in.



------------------------------------------------------------------------------
[This I didn't completely translate from '91 french]

OPTIMIZING


We must distinguish "physical objects" from "virtual objects"
The first have a physical representation in the machine,
with some more or less bizarre low-level encoding.
The other is the user-visible abstraction, that may migrate and change
completely its encoding.
A record can be split in multiple non-contiguous bits, that can be
spanned over completely different physical media (registers of multiple
CPUs, RAMs, disks, etc).
Multiple objects can overlap on the same physical storage, as long as
at a given moment we know which is where.
Procedures with same logical type can have completely different
physical types (= calling conventions) so that the implementation be optimized
Compiler & linker with manage logical->physical mapping.


General procedure:
  First, optimize trees to reduce required execution memory for the stack
and heap. Then, group terms according to the instruction set.

Data:
- multiple data can share the same location (possibly a register),
as long as their lifespans are disjoints.
- data are placed in the best possible order (so that possible fast
instructions like PUSH/POP are taken advantage of)
- in arrays indexed by sets of non-contiguous values, holes can be used.

Branching:
  in "if" or "case" instructions, it can be said that some case is expected
to be more frequent than the other, so that code layout optimizes for this.
This information can be taken from run-time measurements. More thorough
information can be taken, splitting cases according to some contexts,
giving precise ratios for loops, etc.

Timing:
  Optimization can be done out of comparisons of actual (automatic)
measurements of generated code.

Optimized Optimization:
  Optimization can be done simultaneously for all instances of a term,
taking into account knowledge of all the contexts in which it can be
called: some instances may offer more possibilities of optimization.
In some cases, the term may deserve to be split so that it takes advantage
of some particular contexts. Beware both runtime feedback and user
expectations as for ratio according to which to optimize.


Final procedure call:
   CALL A; RET gets rewritten into JMP A when possible.

Stack:
   The compiler decides the order of local variables in heap/stack/records
so as to reduce memory and time consumption.


(!) Warning:
  One of the most important parts of the compiler consists in
determining the informational context for each term/instruction block.
Optimizations are done thanks to this context stuff; splitting context
in multiple cases can be useful, but should be reserved to heavily
optimized blocks (that the user/runtime feedback/compile time AI says is
to be optimized).


Recursion:
  recursion is automatically transformed into terminal recursion/iteration
when possible, even though it may involve higher-order functions: such
functions can be simplified if they are terms in a well-known signature.
In case nothing good is achieved, reverse to the original with a flag
indicating what we did. 
  In any case, determine variables that are constant or variable
accross the recursion. Don't actually pass variables that are not
variable.


Problem: data and information
 solution: everything is dynamic information. data is just information
that has a transiently static implementation.


Problem:  Polymorphism
 - Principle: a logical object is both the representation of several
 possible higher-level objects and an abstract view of lower-level
 "physical" objects.
 - Note that polymorphism can be done
  * at "semantic annotation level" over a regular grammar,
   as is done in almost all existing computer languages in a more
   or less complex way. this means that files are parsed according
   to some context-free grammar, then annotated with their meaning,
   and that the meaning of identifiers is resolved according to
   that polymorphism.
  * at "grammar level" over a well-defined syntax. What is pre-defined
   is the syntax (the lexical analysis), and lexems interact in a
   polymorphic way, with a 
   Except perhaps for puns, texts written in human languages, are
   understandable at this "grammar level".
  * 
 - The above means complexity can ....
 - Question:
   if A is a type of unary prefix operator on type B, and B unary postfix
   operators on type A, how to interpret "a b" where a:A and b:B ?
   Is it possible/wishable that "(a)b" and "a(b)" be used to distinguish
   the meanings ? May be some "operates on" symbol (like mathematica's [])
   or some explicit procedure call (like some structural basic's @) should
   be used.
 - Other Question:
  - How determine the type of a constant in an ambiguous context ?
   A symbol can represent several constants at the same time, according
  to the type of represented object, e.g. 0 that can represent the
  neutral element of any additive group, or the "origin" of a possible
  set on which such groups operates simply (like integers on pointers,
  real numbers on a the affine straight line, etc); thus, even
  if only considering base types, it can be integer, real or pointer,
  not to talk about multiple integer, real and pointer types.
 - Generalizing the problem:
  if there is polymorphismm, how distinguish patterns, so as to
  satisfy both the programmer and the compiler ?
 - Notes:
  The exact compilation algorithm, the order in which it considers
  identifiers, must be well known in advance, so that there is a way
  to know what it will do in case of ambiguity. But the compiler can
  have add-on modules to detect ambiguities and generate warnings.
   In the graphical version, the UI will ask the programmer to
  distinguish between alternatives.
 - Proposed solution:
 1) do a linear search, only accepting as an identifier the first matching
  name, and generate an error if it isn't good.
    This is what most people do, but it's not satisfying, because operator
  overloading looses its essence: one cannot use the same operator for
  more than one thing at a time.
....
   Cette solution n'est pas du tout satisfaisante, car alors, la surcharge
 des oprateurs perd toute sa substance : on ne peut pas utiliser le mme
 symbole pour deux choses  la fois, puisqu'une seule est accepte  un
 moment donn ; par exemple si a est dclar comme fonction oprant au choix
 sur un entier ou un rel, le sens entier tant le premier dans l'ordre de
 recherche, et si b est un entier, alors l'expression  a (b) , ayant pour
 unique sens celui de la fonction a oprant sur l'entier b, sera refus !!!
   Sinon, il faudra une syntaxe spciale pour les fonctions, qui obligera
 notamment l'utilisation massive de parenthses, et de toute faon toute
 expression tre barde de parenthses, et la priorit des oprateurs sera
 impossible pour des oprateurs sur types diffrents.
 2) Tester toutes les possibilits, et s'arrter  la premire qui marche, ou
 continuer, et gnrer une erreur (ou demander  l'utilisateur de choisir
 celle qu'il voulait) si plusieurs significations sont possibles.
   Cette solution prend du temps (algorithme en n.exp(p) si p est le nombre
 d'identificateurs  la suite sans parenthse), et devient dmentielle si
 l'on ne peut pas dterminer la fin d'une expression. De plus, elle demande
 que la spration des identificateurs soit cohrente. Aussi, il faut que
 la rfrence  des identificateurs d'autres modules ne soient pas atteints
 par la concatnation d'identificateurs voulus. Il est donc prfrable de ne
 pas permettre d'espaces dans les identificateurs, ou en tout cas d'en
 limiter l'usage, ou bien de limiter strictement l'accs aux variables,
 de faon  ce que les identificateurs de variable non utiles ( critre
 laiss  l'apprciation du programmeur ) ne viennent pas interfrer avec
 ceux des variables utiles, notamment en modifiant la sparation du texte
 en identificateurs. Peut-tre faudra-t-il sinon tudier tous les dcoupages
 possibles du texte en identificateurs, de manire  dterminer le bon ( en
 esprant qu'il y en a un unique qui convient ).
 - Le problme du dcoupage de la phrase en identificateurs est plus
 compliqu qu'il n'en a l'air : en effet, certains oprateurs, comme "." et
 "de" (ce dernier tant sujet  controverse), modifie la visibilit de
 donnes vis-a-vis du compilateur. Aussi, ou bien il faut compliquer
 totalement la procdure de dcoupage, ou bien il faut se rsoudre  refuser
 les espaces dans les identificateurs pour sparer ceux-ci, ou bien enfin il
 faut refuser l'oprateur "de", et modifier dynamiquement la visibilit
 devant un ".".

Problme: CONSTRUCTEURS ET DESTRUCTEURS
 Qu'en est-il de constructeurs et destructeurs en OSL ?
 => regarder leur utilit vritable en Pascal Objet et en C++.
  = lier et dlier les pointeurs avant/aprs utilisation.
  = Tout le problme est la cration ou destruction "simultane" d'objets,
  la modification d'un objet fortement li  d'autres, ou li  beaucoup
  d'autres. Le problme se fait sentir avec le paralllisme: il ne faut pas
  qu'un objet utilise des donnes pendant qu'un autre est en train de les
  construire ou de les dtruire, et que leurs liens sont dnus de sens.
 => voir si OSL ne peut pas le faire automatiquement.
  = on peut gnrer automatiquement les constructeurs dynamiques correspondant
  aux types de donnes prenregistrs. Nanmoins, on peut prvoir des
  problmes lors de la "fusion" de deux types logiques qui demanderaient
  l'initialisation de champs dans un ordre incompatible (rflchir  ce pb).
 => un problme est quand on a un type de taille variable. Mais on peut
 toujours s'arranger de la manire suivante:
 1 ) si la taille maximale est assez fortement borne, la rserver.
 2 ) sinon, utiliser un pointeur, qu'il faut toujours initialiser avant de
 le transmettre.
 => Il ne faut jamais utiliser d'objet non initialis. Si de tels objets
 peuvent exister, il faut concevoir un systme (avec drapeaux, etc)
 permettant de prvenir l'accs  des donnes non initialise ou en cours
 de traitement.

Smantique :
 - Une fonction n'est jamais qu'un oprateur prfixe sur une liste de
 paramtres.


************************ POSSIBILITES DU LANGAGE ****************************

Ton plus ou moins impratif:
 on peut affirmer avec plus ou moins de force (de ferveur) des dclarations
en OSL; on peut aussi donner des ordres plus ou moins impratifs au compilo;
les plus impratifs prdomineront (non sans que le compilo grogne d'autant
plus que les ordres auront t contradictoires et que son avis aurait t
diffrent sans les ordres incrimins).

Traitement des ERREURS et cas exceptionnels:
 - Ce traitement devra tre facilit pour le programmeur, des (mta-)routines
 standards se chargeant de la communication de ces erreurs, etc, avec une pile
 des routines grant les erreurs, (chaque routine passe la main  la
 prcdente si elle n'est pas concerne par l'erreur), une procdure d'erreur
 dclare sans effet de bord bien qu'elle puisse modifier une variable
 globale indicatrice d'erreur, de manire  ne pas gner l'amlioration du
 code. En effet, une procdure appelant une procdure  modifiant une variable
 globale est elle-mme indique comme la modifiant, si bien qu'elle
 ne commutera pas avec une procdure lisant cette variable (attention  ce
 que certaines procdures modifiant une variable doivent parfois
 implicitement la lire: l'oprateur += en est un exemple), alors que deux
 tests d'erreur fatales devraient pouvoir commuter, car si de telles erreurs
 surviennent, peu importe puisque l'ordre dans lequel elles sont testes.
 - pour qu'il n'y ait pas de syntaxe spciale pour les macros, on peut inclure
 des fichiers habituels qui dfiniront des mots-cls permettant "d'allouer" un
 numro d'erreur, de grer des objets erreurs, etc.

EXCEPTIONS :
 - On doit pouvoir dfinir des cas d'exception dans le langage: par exemple,
 une entre d'une base de donne peut tre une date de 1945  2099, ou bien
 "avant" ou "aprs" (on doit aussi pouvoir demander que s'il reste de la place
 disponible dans le codage, on accepte des dates au-del de la limite minimale
 que constituent '45 et '99). De mme, la division de deux rels renvoie un
 rel ou bien une exception si le dnominateur est nul. La fonction de prise
 de l'lment suivant d'un ensemble doit pouvoir rendre un rsultat spcial
 quand l'ensemble est vide; etc.
 - Remarque: je m'aperoit (bien tard) que cela existait en C++ (vritable)
 et en CAML.

RESTRICTIONS :
 - On peut avoir des types comme cas particuliers d'autres.
 Par exemple, aprs avoir dfini le type complexe en fonction du type rel,
 on peut faire hriter le type rel en question du type complexe, avec la
 restriction que la partie imaginaire est nulle.

Adressage :
 - toute donne virtuelle doit pouvoir avoir un accs virtuel dans de la
 mmoire virtuelle, et rciproquement. C'est plus drle comme a.


Dclarations :
 - On doit pouvoir dclarer une variable d'aprs sa valeur par rapport  une
 autre. Par exemple une dclaration   b = a + 1  fera qu'en toute
 circonstance, un appel  b renverra a+1, et une affectation  b affectera 
 a la valeur donne moins un.
  Bien sr, pour permettre une affectation rciproque, il doit y avoir des
 restrictions, la fonction associant b  a devant possder une rciproque.
  De plus, on doit pouvoir demander que b soit ou non effectivement stoqu
 de manire permanente en mmoire, ou recalcul  chaque fois.
 - On doit pouvoir dclarer une variable locale comme tant stoque de
 manire externe  sa zone de dfinition (plus gnral que le static du C);
 par exemple, dans une donne, on peut demander  la taille d'tre stoque 
 part, ou dans l'quivalent d'une union du C, ou d'un case of du Pascal,
 crire la variable de conditionnement  part, voire de manire commune 
 plusieurs objets.
 - On doit pouvoir explicitement demander qu'une variable change de place;
 cela revient souvent  utiliser plusieurs variables, mais ce n'est plus
 rigoureusement exact aprs que l'optimisation ait fusionn plusieurs
 variables, etc. Cependant, la dite optimisation doit pouvoir communiquer
 ce qu'elle a fait, pour dboguer le programme (ou l'optimisation) avec des
 variables de haut niveau, ou pour transmettre  un compilateur moins
 optimisant, voire en prvoyance d'une compilation aprs (lgres)
 modifications, qui pourra rutiliser l'acquis des compilations prcdentes.


Smantique : Pour des raisons de simplification d'expressions gnrales par
 le compilateur, il existera un type vide (comme le void du C) tel qu'un
 pointeur sur le vide soit aussi vide.
  Ainsi, dans une mta-liste de paramtres, on pourra inclure des paramtres
 se rvlant inutiles dans des cas particuliers, et qui seront limins.
  Le type pointeur gnral ne sera donc pas un pointeur sur le vide, mais un
 pointeur sur l'inconnu.


AieAieAieAieAie !!!!!!!!!
   Dans une expression, comment prendre en compte les constantes de types
 construits ? En effet, si un type construit peut tre utilis dans une
 expression (puisque le leitmotiv du langage est de ne privilgier aucun type
 par rapport aux autres, tout type doit pouvoir avoir une possibilit
 d'entrer et d'utiliser simplement des constantes).


Automatic definitions:
  in packages, there must be a way to define objects one from the other;
at binding time, the best definition among the possible ones will be
taken. This is just not possible in existing languages, and involves
very little logic/backtracking/AI. Trivial example: to define addition
on some space, we may equivalently give a pure operator +, or a side-effect
operator +=, and one is automatically defined once the other is. Sometimes,
it is better to give completely different versions for each. There should be
a way to do all that very simply. Some definitions in the package may be
optional or required for the package to be meaningful.


Affichage :
  Voici une technique utile si l'on veut afficher les rsultats de calculs
 au fur et  mesure sans que le compilateur croie que l'ordre dans lequel
 les rsultats sont affichs est affect par l'ordre des calculs: dclarez
 une sortie locale de donne dans chaque procdure de calcul, par laquelle
 transitera les rsultats de la procdure; dclarer ces sorties indpendantes
 (mme si ce sera parfaitement faux). Dans un autre module (dans le mme, a
 fait plus sle), faire les sorties vers l'utilisateur. Relier les deux
 modules de force.

Attributs de bas niveau:
  on peut associer  un objet de haut niveau des attributs concernant son
 codage en bas niveau; on peut demander au compilo de s'interesser ou non 
 telle ou telle sorte d'optimisations; on peut lui demander explicitement de
 dvelopper ou non une expression gnrale pour certains cas particuliers
 (exemple: pour la fonction de Ackerman, on gagne beaucoup  dvelopper selon
 les possibilits de la machine en n=0,1,2,3,(4)). On peut forcer le compilo 
 considrer ou non que tels objets seront ou non accds depuis l'extrieur de
 la partie actuellement dfinie.

Notes complmentaires sur un objet:
 on peut crire  propos d'un objet des notes complmentaires en gnral,
concernant d'autres aspects de l'objet,  d'autres niveaux (ni forcment plus
bas, ni forcment plus lev, ni forcment gal); par exemple on peut proposer
un codage de l'objet dans un autre langage; mais l'objet n'y sera pas
rigoureusement quivalent  ce qu'il est en OSL: il pourra contenir plus ou
moins d'information. Justement, on peut prciser que telle fonction est ou
non injective, surjective, inversible en un temps raisonnable, etc.




*Compiler internals*

Commute:
  * the most common technique for local code optimization is commutation of
  basic operations: we note what can be done in parallel, and we
  exchange operations even if it makes the code a little more complex,
  as long as we can revert to the original if the gain is not large enough.
  * input values are important only if read, and output values only if
  actually outputed (lazy evaluation). If some location is time-shared
  between several (constant) values, beware to save/restore values so
  that to follow calling conventions for each operation, and keep everything
  consistent during local operation exchange. Take that into account for
  efficiency questions.

Identifier lookup:
 typically, this can be hashed and parallelized, even though the semantics
 is defined in a serial way. Now, this technique may involve large structures,
 and is not very good for reflectivity and higher-order contexts. A compromise
 must be found at run-time, but shouldn't affect clean semantics.


OTOP:
- software protection in source/portable code:
   Firstly, some trivial protection/encoding scheme to discourage trivial
 hackers. Then, more difficult things.
   The source program is semi-compiled and encoded in such a way that
makes reverse engineering impossible: all unpublished names and
debug information are forgotten; some macros and functions are expanded,
other are introduced; code was globally optimized, but locally unoptimized;
code is compacted and a self-extractible at compile-time; all "static"
variables are made globally allocated with unique name; data location is
systematically moved with the program flow; more generally, data encoding
changes with code pointer; critical data (life points, etc) is encrypted
especially to forbid cheats; signatures are inserted so that encrypted
code is numbered and identifiable. If we apply those to C/C++, we're sure
to win the ioccc !
   But now, what about proofs ? The most "secure" way for the publisher is
that there be some independent organization that checks proofs and puts
its securely encrypted seal on approved software. The organization must
be very trustworthy, and its seal impossible to fake (lots of bits of
PGP signature ?). Else, the proof comes compiled, too.



------>8------>8------>8------>8------>8------>8------>8------>8------>8------
------>8------>8------>8------>8------>8------>8------>8------>8------>8------

##############################################################################
elm -s "HLL semantics -- a higher-order reflexive dynamic system" tunes <<'END'

1) Logic
--------
   Well, firstly, the easiest way to mix programs and logical specifications
is to use the Curry-Howard type<->proposition isomorphism, as in the Coq
proof system (cf. ftp.inria.fr or www.inria.fr). That is, an element of a
given type is isomorphic to a proof of the corresponding proposition "this
type has an element". This may seem quite trivial, and actually manipulating
and verifying proofs do become trivial; this triviality is *exactly* the same
local triviality that mathematical logic have: proofs are trivial to check
and generating new theorems is also trivial; but as in mathematics, writing
a program that fulfills some given specification, which corresponds to finding
a proof for given theorem, is a difficult undecidable art, as soon as you
have powerful enough type/program, or equivalently proposition/proof
constructors. Local triviality is *not* global triviality.

2) Basic type constructors for logic
------------------------------------
   Now, what constructors do we *need*, outside any optimizing considerations
(up to even huge constant factor) ? Let me first agree with the choices of
the Coq proof system. Well, at least we need:
** lambda-abstractions:
  We some mean to create functions by "abstracting" a variable in an
arbitrary expression, that'll become the function's parameter (option:
multiple parameters at a time). The converse deconstructor is function
application ``fun arg'', by which by which you apply a function ``fun''
to a properly typed argument ``arg''. The type of a function is a
universally quantified type: if expression ``e'' (where variable ``x''
can appear) is of type ``E'' (where variable ``x'' can also appear, why
not ?), where ``x'' is of type ``T'', then function ``lambda(x:T).e'',
that I'll write ``[x:T]e'' according to Coq syntax, is of type ``(x:T)E'',
which, if ``E'' does not depend on ``x'', can be written ``T->E''.
Allowing the type to depend on the variable is having *higher order* logic.
Most existing languages only allow types to depend on type variables, so
they can achieve static and decidable type checking; but there is no reason
why it should be so. Coq does not create such difference between types and
other objects, but preserves decidability by requiring some foreknown
decidable process to conclude to type equality to type check successfully.

** (Mutually) recursive definitions:
  That's how Coq does it all.
You define a list of objects/types as the least fix point of some type
operator where each type T can be described as a list of constructors for
that type; a constructor being a function with n arguments returning an
object of type T.
  The deconstructor is an "induction" function that allows "reasoning by
induction"; it's an axiomatic function that builds an T->U function from
functions of type Ti->U where Ti->T were the types of constructors for T.
The function gets resolved by applying the right subfunction for the right
case.
  Example: if type List is defined as
``let list = [T:type] (Empty: (list T) | Cons: (T -> (list T) -> (list T)))''
then there are two constructors, Empty and Cons that construct a ``list T''
one from nothing and the other from an element of ``T''. The deconstrutor
is an induction function
``list_ind : U -> (T -> (list T) -> U) -> (list T) -> U''
such that if ``f = list_ind fEmpty fCons'' then ``f Empty = fEmpty''
and ``f (Cons x l) == fCons x (f l)''

  Such definition provide at the same time the facility of
structures (each constructor is such a structure), case-unions (lists of
constructors), and pointers (through recursive definitions).
  Unhappily

 
** Structural constructors:
  construct an object from a list of others; define a type as the objects
you can obtain by applying some axiomatic constructing function: for
example, you assume to have an object Cons:A1->A2->...->An->B as a constructor
of type B from other types A1...An (note that "->" is right associative).
The deconstructor is some axiomatic function that constructs a function B->C
from a function A1->...->An->C.
** Union constructors:
  construct an type from several cases. An object of the type is obtain
from one of the constructors:
Case_type a1:A1 | a2:A2
A1|A2|2
 this is the usual ML let rec constructor. For example, you can define
(using some least fix point technique), a list of objects of objects of type
T as 
let list = [T:Type] Fix_point [L:Type](Case_type Empty:L | Cons:T->L->L)
Actually, the Coq proof system groups the three previous into one so as
to achieve easier syntax and much less function application.
** Unique object generator:
 Some means to create a new, unique object; else structural equivalence will
apply when comparing objects, that may compare as equals objects that we
want different.


3) Adding usability an efficiency
---------------------------------
   All the above are quite good. But they become quite unusable as is,
as they involve a quite heavy syntax. I dunno how the latest versions
of Coq does it.
** Typesystem extensions:
  
** Side effects:
 The previous logic is perfect, but lacks side-effects. It's called "pure";
but won't lead easily to efficient description of behavior 
There should be a way to ease the description of such side-effects as
meta
** Note that Lisp's objects could be object with only one type much like this:
Inductive Lisp =
	  Cons : Lisp -> Lisp -> Lisp
	| Symbol : string -> Lisp
	| String : string -> Lisp
	| Integer : int -> Lisp
	| Float : float -> Lisp
	| Vect : Lisp vect -> Lisp
** Lazy evaluation:
 one of the reasons why the ML (CAML and SML) systems suck rocks is because
they have strict evaluation, which means all arguments are actually evaluated
before a function is called, even if they are not needed. Thus, if you want
to take advantage of some higher-order function, i.e. a type morphism,
you'll have to compute the whole value of the image of a structure
by the morphism to use a very partial deconstructor on it !
[e.g. to explore the rightmost branch of a tree through a morphism that
swaps right and left, using a leftmost branch explorer, you must rewrite
the whole tree !!!!].
So higher-order functions are too expensive to use under ML,
and nobody use them. ML is not really a language with the power
of higher-order.

  For example, 
** Non least fixed points:
** Axiomatic constructors:
 To allow interfacing the logic to an efficient implementation, we need have
access to external/lower parts of the system from the logic, through the use
of such axiomatic constructors. Logical axioms must also be added to describe
the behavior of our objects.
some me constructor built in built in some external (lower) part of the system.
  For instance, it could be a constructor for the built-in integer type.
 Other constructors 
 

3) 
--------------------

4) Lazy evaluation
------------------
* The one reason why ML sucks is that it has *strict* evaluation.
Which means when you have structural isomorphism, it is far too expensive
to 


4) Reflexivity
--------------
   All this means that we do need some typing mechanism (sorry Chris, but
that's the only easy way I know to mix logic and programming). But of course,
according to the level of specification you need and the language you use,
it may be interesting to vary your type systems: some people may want very
rich type systems, perhaps undecidable semi-decidable ones, where no
additional logic is needed for specification; some others may prefer a very
poor (low-level) type system, and put any kind of specification outside
(except perhaps for behaviour relative to garbage collection and/or side
effects). I think this may be done easily by having not only types, but type
*systems* as first-order objects, i.e. having a *reflexive* system.


x) Other features
-----------------
   Orthogonal persistency of objects, and resiliance to shutdown are useful
features that we may require any implementation to provide, so programmers
shouldn't have to worry about saving to files with error recovery, etc.

y) Comparing to other systems
-----------------------------
   The logic makes the system suited for proof verification, which most
existing language systems do not allow.
   As for BETA, I (unhappily) didn't study it enough to formulate an opinion.
Please explain how it's similar or different from other unified systems (say,
SELF, or ML).
......

END

------>8------>8------>8------>8------>8------>8------>8------>8------>8------
------>8------>8------>8------>8------>8------>8------>8------>8------>8------
------>8------>8------>8------>8------>8------>8------>8------>8------>8------

#elm -s "HLL semantics -- problems" tunes <<'END'
#
#END



##############################################################################
elm -s "HLL semantics -- sample implementation" tunes <<'END'
   Now, Mike wanted some sample implementation; so here we go, for my
current vision of how it could be done on the i386 and other similar
architectures.

   At the lowest level, there is an abstraction of the CPU, which is a
machine with a finite number of global variables (=registers), and that
operates on "continuations", by stepping instructions.
   For instance, on the i386 architecture, the global variables would be
the E?{[A-D]X,[SD]I,[SBI]P,FLAGS,FS,GS} registers (assuming flat space for
the other registers). The processor just evaluates instructions and continues
execution by modifying the EIP register.
   I think we can assume that a continuation passing computing model maps
well any machine architecture our system will be implemented on (for some
time, at least); but this model maps perfectly to any current or soon to be
CPU, so everything is ok. Now, there is more than one way this mapping can
be done, assuming different register use/save conventions. So we must
provide both a standard way to use the registers on our implementations,
and ways to describe *any* convention, and bridges from one to the other.

   Now, we know (from previous discussion) that there *must* be some
garbage-collection mechanism, and thus GC conventions, for the system to
be persistent. And a count-based only system is *not* enough, as it is not
lively: one may easily create cross-reference loops, and fill unrecoverably
the memory with them, unless we have even stricter conventions that ensure
the pointer graph is a tree !
   To achieve a light-weight GC, I'd propose giving away one bit on integers
to differentiate them from pointers.
   So either we have some heavy (recent) C++ like systematic type tagging
with infix pointers, or

   As for the i386, I'd propose we use ESP to point to something that would be
both a quick allocation heap, and a zone for temporary storage (local stack
that'd have to be cleaned up at actual PAUSEs). Using virtual memory
continuations are implemented using machine registers.
Each low-level object may have its own calling convention, specifying what
register must contain what argument, etc. For example, a variant of the
integer "add" function could be just an "add accumulator,constant" assembly
instruction, with proper constant, an calling convention using the
accumulator, while the instruction pointer would contain the current
continuation. Another variant could use a FORTH double stack, etc.
   To allow garbage collection of code objects, I'd propose.....
.....
END









------>8------>8------>8------>8------>8------>8------>8------>8------>8------

##############################################################################
elm -s 'Re: Half-baked UI goals/proposal
In-Reply-to: <>' \
tunes <<END
END
##############################################################################
#elm -s 'Re: 
#In-Reply-to: <>' \
#tunes <<END
#END




cat << END > /dev/null
END




   As you may already know, I'm trying to develop the concept
of "active annotations" as the basis of the TUNES HLL.
I'd like you to help me refine this concept and find the flaws
in my current views.

   So, as the basis of the HLL are objects. What are objects ?
Well, at the time being, they may be considered as axioms. They are
anything that could be constructed with available operators. A system
integer, a symbolic equation, a database, a remote object accessible
through a key, the would-be result of applying some function to some
argument, all these are objects. Everything is an object.

   Then, what differentiates objects ?
Considering only the above, objects are like names. They are like words,
names, symbols and terms, that don't have any intrinsical meaning. Just
What gives an object *meaning* ? Just like what we learn in math, the
symbol is nothing; not even single objects have any meaning. What counts
is a *structure*, that is a set/collection/universe of objects related
by structural relations. Thus you must always consider not an isolated
object, but a structure made of objects and relations between these.
You may talk 

   As an example, the set of rational numbers, with their usual order
as sole relation, is a very simple structure. As one sees, the logical type
of every object is the same as that of every other: every number cuts the
set of rational numbers in two parts, and objects are undistinguishable in
this respect. But as soon as one considers simultaneously several numbers,
one can compare these objects, and thus will not consider just the 1-type
of these objects independently, but the n-type of all these objects
concurrently, and there are more than just one 1-type: for example,
there are three 2-types (either a<b, a=b, or a>b).


 you may see, all objects
are stricly equivalent in the structure.



What is important about an object is not its name. Somehow, object

 the relations it has with other objects.





Annotation resolving:
* package A has objects X,Y,Z written in language L
   package A has some optimized version A' with objects X' Y' Z' in assembly
   language for CPU C, which is equivalent to A through isomorphism phi.
   Now, X can be migrated to a machine w/ CPU C w/o Y and Z being migrated.
   To run, it will be compiled to some assembly version X", using some
   standard calling convention to communicate with Y and Z. Now, X" and X'
   are both using CPU C, but using different calling conventions. Thus, the
   annotation for X' and X" must be different, and not just "version for C".
   are both 

------>8------>8------>8------>8------>8------>8------>8------>8------>8------


<LI>There are three different mechanisms:
	1) specializing
	2) modifying
	the second one can be seen as specializing a stream which is
	partially visible depending on some time axiom, and/or
	exploring objects with some unicity (linear) type.
	3) abstracting the context, by which we consider some eeven higher
	theory in which everything before was expressed.


------>8------>8------>8------>8------>8------>8------>8------>8------>8------
Let's define a memory manager in a side-effective style:
* there is a "memory pool" that contains information.
* Information is quantized in blocks, most commonly multiples of machine
 words, themselves multiples of bits.
* there is a function to grab information area from the pool,
 according to information on the target object's type, size, expected
 liveliness, etc.
Now
* a manual manager also has got a function to free an area allocated for
 an object that we know won't ever appear again in computations.
* a garbage collector can automatically reclaim such areas.
The essential conditions on memory managers are
* conservation: information allocated will remain at least for the time
 of the
* liveliness: the amount of useless information in store won't grow
 indefinitely large.
 

------------------------------------------------------------------------------
mem:
a one big physical pool; other pool logically allocate from it, with only
overflow management that changes. This is safe because the language is
referentially safe.

rt:
persistent rt tasks have to allocate the time slice for copying their data
into buffer for synchronized persistent store to perform ok.



i386 stack/heap:
  by having some system-mode only pages below the current user stack limit,
we can achieve shared system/user stack without risking stack overflow at
next interrupt.
  Another trick to catch heap overflow would be to use debug registers
(do they exist on Pentium's and PPro's ?), which would allow everything
to take place without priviledge switching !
Where do I get those silly ideas ?
  not to eat up time loading registers, keep DS=ES=DPL3 thingy,
priviledged users can directly (dis)allocate memory by accessing the mapping
table.


what makes a language more informative than another is the
pertinency space of implicit contexts, and the ease of its construction.
(strongly) Typed languages,
with type as part of the context, are thus more informative than
otherwise untyped languages, if the type space is rich enough.


otop:
* word registering
* word interpreting ?
* word compiling
* word defining runtime




------------------------------------------------------------------------------
To Glossary.html



<LI><A NAME="parser"><H3>Parser</H3></A>
<DD>
	A parser is a piece of software
that makes complex structured data
out of text streams (streams being minimally structured data).
<DD>
	Because the standard computing models
place unstructured text streams as the standard interchange format
(which was quite understandable in early times of non-interactive,
standardless computer architectures of the heroic ages),
parsers play a very important role in all computer software.
	But would expressive enough free interchange formats be available,
together with easy ways to interface them to programming languages,
parsing would become again the special purpose it always should have been,
...
<P>
<BR>


<LI><A NAME="compiler"><H3>Compiler</H3></A>
<DD>
<LI>When a <A HREF="#parser">parser</A> and
a <A HREF="#code generation">code generator</A> come packaged together,
one can say that the package is a compiler,
or that it includes a compiler (as it can include more software even).
<LI>
*   These functions are logically linked but quite distinct,
as are constructors and destructors (of which they are a particular case)
*
but quite distinct,
though logica
   In the traditional computing models,
a compiler.....
*
   Compilers as separate entities
were born at a time
when computer memories were too small
to hold at the same time an editor, a parser,
a code generator, and generated code;
they are adapted to batch processing,
and their value still lies when and only when
resources of the target system
are very valuable at the time the code is run.
-->
<P>
<BR>


------------------------------------------------------------------------------
  
* Layering the design is like
 edicting laws of nature that people should follow:
 either you repeat natural laws, in which case you lost your time,
 because people would follow these laws anyway,
 or you make mistakes at doing so, and then you lose the time of
 all the people that you force to obey wrong laws.
 In any case, it's a loss.

 So sure in Tunes there will be object hierarchies.
 But they won't follow any arbitrarily decided layering.
 If any layering is to be, it may be deduced from actual code,
 not actual code deduced from it.

* Object = information container + points of view
 (mind the plural)

* inheritance fails because a same information
 can be viewed in many ways as matching some pattern
 

* Today, batch processing is the worst of both old and new computing world:
 coarse-grained processes run on single, coarse-grained nodes,
 and need be programmed separately. It is thus purely harmful,
 as it is slow, requires lots of resources, and provides little security.

 In the time of parallel computation,
 batch processing can retrieve its original utility,
 which was to save precious resources on memory-poor systems:
 it allows to take advantage of fine-grained nodes with small memories
 of of masspars and networks,
 while it needs not be coarse-grained,
 nor does successive jobs need be programmed separately;
 on the contrary, batch allows to run with little local code memory/cache,
 and separating programs into small batch jobs can be done automatically
 by the compiler.


continuations should contain not only the static environment,
but also the dynamic environment, as defined by fluid-let
and modified by set!
this should be doable with any kind of getter/setter!
hence, each thread has a list of "variables" to save/restore.
Now, it is stupid for threads to save/restore absolutely everything
when an "akin" thread is called in place, that shares most/all of the
environment. Sized Linked lists should allow to easily detect
such kind of similarities.
On the other hand, when saved variables are part of memory that
isn't accessed/accessible by the new thread,
(e.g. because a enclosing variable was switched),
it may be unnecesary to actually save them.


So the reflective system has a current evaluator:

the standard interactive evaluator will cache function results
according to distance from user loop:
directly user-asked results are twice as important as lesser results, etc;
importance of a result is cached, and adds-up,
until some result reaches the maximum rate or the cache grows full,
at which time all importances are shifted;
a roll-over exponent can be used for that.
Very important things are optimized.

epsilon fillings should be systematically cached (?)
Well, at least, there must be a cached value for
actual execution to take place...


So the HLL- should provide:
* computations: lambda calculus
* side effects: references
* modularity: "objects" as sets of bindings
* specifications: constraints, checking
* reflectivity: epsilons, tactics

Further services include:
* reified multithreading: call/cc, ...
*

Built upon it:
* pattern matching
* macro system

Constraints:

------>8------>8------>8------>8------>8------>8------>8------>8------>8------
It would be nice to have a tactic that strips down
proofs to their simplest form,
and allow to deduce the most general structure
for which the "same" set of proofs holds.
------>8------>8------>8------>8------>8------>8------>8------>8------>8------
On MMU-able machines,
a way to implement implicit hardware-checked locks
for coarse-to-mid-grained objects
is to have the locked-object's code MMU-replicated at various addresses,
with only the valid ones being enabled for execution.
Hence a jump/call to the critical section would implicitly
verify that the lock is free.
Interrupt recovery code for the section would propagate any needed changes.
Because only one address is valid at a time
on the whole distributed system, we're safe.
   More generally,
MMU protection is good for one and only one thing:
mid-to-coarse grained (both in time and space) invalidation of objects.

------>8------>8------>8------>8------>8------>8------>8------>8------>8------
Security:
  security through visibility
    visibility of access filters only
      unforgeable keys
        one-time-keys

Functional OO Reflective Proof-checker
Krill Really Is a Light Lisp
Kernel Lisp U* G* E*
Reflective I* Lisp for Kernel Environments

Lisp Users' Reflective Kernel [E* R*]
T Reflective I C Kernel Lisp E
R I L K

------>8------>8------>8------>8------>8------>8------>8------>8------>8------

For compiler optimizations:
1) learn from well-known optimizations:
 the guys at the Ultra project have begun a web page on that,
 that could serve as a repository;
 people on comp.compilers are very knowledgeable, and can give good advice;
 lots of CS conferences (POPL, PLDI, ISMM, etc) and journals
 contain articles describing various techniques;

2) Get inspiration from hand-coded optimizations:
 from every nifty implementation trick we use in any program,
 we can extract a most generic automatic implementation technique,
 that may be used in similar (however rare) settings.

example:
  unique typing without return allows for in-place update;
  unique typing with unique return allows
	for dynamic environment (unwind-protect style).

  differential coding allows to save space or time;
  it can be used not only as a punctual code transform,
  but as a global functorial code transformation.

  cacheing works well with differential coding

  objects are usefully grouped into "types", or "cases",
  with "subtypes" and "subcases" that refine them
  (sometimes in orthogonal ways -- good)
  according to a very broad range of criteria.
  the types that the optimizer wants are those that allow for optimization,
  that is, for simpler encoding strategies (which subsume memory management).
  Of the types and cases that need be handled, we have, with recursive truth,
  an inverted 80/20 rule, such that most of them can use a uniform
  generic encoding, but some of them require lots of sophistication for
  performance (within the limits of the machine's resources,
  particularly caches).

  for instance, stack frames might come in various sizes;
  but intensive computations happen in a few routines,
  and use few sizes at once;
  intensive recursion will thus use few frame sizes, perhaps only one;
  transforming recursion for frame optimization
  amounts to transforming recursion in iteration,
  for those particular routines (tricky in presence
  of higher-order constructs).

  [BTW, I couldn't resist tackling a stupid problem for math weenies:
  solve the inverted 80/20 rule, inverted 90/10 rule, and all other
  inverted (1-p) vs p rules. We may formalize it as follows: find a normal
  distribution f:[0,1]->R+ such that whatever the focus of the [0,x] of the
  x*100% of first considered cases, the first p*100% of them have relative
  frequency (1-p)*100%. We get the equation (where f is the density function):
  sum(0,p*x,f) = (1-p)*sum(0,x,f). A rot-13 solution follows:
  jura jr qrevingr gur nobir rdhngvba, jr trg c*s(c*k)=(1-c)*s(k), be
  s(c*k)=(1/c - 1)*s(k); guvf fhttrfgf n ybt-ybt fpnyr, v.r. fghql bs
  u(l)=ybt(s(rkc(l))); jr gura unir gb svaq n u:E->E fhpu gung u(l-p)=n+u(l),
  jurer n=ybt(1/c - 1) naq p=ybt(1/c); gur trareny fbyhgvba sbe gur rdhngvba
  vf u(l)=-(n/p)*l+cuv(l) jurer cuv vf n p-crevbqvp shapgvba. Yrg e=n/p gung vf
  e=ybt(1/c -1)/ybt(1/c); e<1. Yrg cfv(l)=rkc(cuv(l) (p-crevbqvp, gbb); jr trg
  s(k)=cfv(ybt k)*k^-e. Gur fvzcyrfg pnfr vf jura cfv vf pbafgnag, va juvpu
  pnfr gur nqqvgvbany pbafgenvag fhz(0,1,s)=1 tvirf cfv=1-e, fb gung
  s(k)=(1-e)*k^-e]

------>8------>8------>8------>8------>8------>8------>8------>8------>8------

Scheduler:
  for good interactive response, have a notion of who "has focus",
  and transmit "focus" along with data...
  the notion of "focus" can be included in the type system,
  and lead to appropriate compilation techniques,
  (which can be obtained by MIXing the according interpretation technique,
  as with MIX Interpreter ==> Compiler)
