The TUNES HLL Specifications

Standard Library of language extensions

This should consist of instances of meta-level architecture components, and uses of these to provide some convenient environment for expressing enough of present-day idioms (such as shell syntax) for the purpose of retaining the conveniences they serve. Gradually these should be incorporated into a larger, regularized framework.

Implementing Annotations

There is the problem of the efficiency of dynamic annotations, all the more when it comes to annotating light objects: the mechanisms to annotate terminal ("leaf node" or "immediate-value") objects.

Reserving an annotation list for each object would be quite too costly (and bloated), and should be reserved for objects that are more often manipulated by humans than used in actual computations (i.e. the objects nearest to the human user).

So annotation mechanism depends on: the annotation object, the annotated object, and the context meta-object. Some aspect of the context chooses the implementation of the object based on a combination of convenience of this decision's computation as well as the foreseeable uses of the object. If an object only gets used once, there's no justification in spending run-time resources to calculate anything but a dead-simple representation.

It seems that somehow, the system should (dynamically) differentiate coarse-interfaced objects (including single integers that are being watched by external humans) from lightly interfaced ones. The former would have an "annotation" field, while the latter would have a more expensive mechanism for annotation (e.g. using hashed association tables), or would have to be annotated through a coarse-object wrapping. So some combination of compile-time and run-time heuristics and architecture would be of use in handling each object differently.

If annotations are annotated, the simple and naive solution is to make the first notation a fuller object instead of a simplified encoding. It is also possible that some simple recursive system could be encoded with a minimal tree-encoding. In general, many things are possible, and this could be subjected to the Tunes system general data structure system.

We must have some efficient implementation to track dependencies when there is partial evaluation, so that no invalidated code is run. Note that this could encompass any kind of system-building ("make") utility, and is much better, as it supports a fine grain and exact semantics.

Keeping Proofs Tractable

Most importantly, all objects should be defined clearly in an abstract context that shows the "limits" of the object: an object is a term made with implicit and explicit parameters. Though some implicit parameters may be instantiated, they can be migrated; though explicit parameters may be future values, they cannot be migrated. These concepts naturally extend to all meta-objects for a considered object.

Now, we want to be able to say things about the constructors that an object was made with. We want to be able to determine a superlist of constructors used to make an object, and/or to say that the object can be expressed in a theory that does not include some constructor.

The problem is when we use meta-constructors that can be eliminated (by e.g. beta-conversion, inference, or more generally higher-order term rewrite), but that we don't want to be actually eliminated. Such meta-constructors are part of the implicitly instanciated, migratable meta-constructors for the object. Typical example: though we define "true" natural integers by Peano's axioms, we want to use usual logarithmical size representation to manipulate them. So even if we discuss about a type Nat=0|S:Nat->Nat, we can manipulate usual system integers, and other such types.

To merge:

The system must provide as part of language semantics access to a choice axiom concept, similar to the epsilon calculus. We explain this choice axiom:

The language must require as few redundancies as possible: a programmer may want to give enough information to the system to specify the program or desired result. The system or interface context should be capable of filling in missing redundant information all by itself. Further hints may be requested from the user and provided to extend this.

There is a mechanism for that, which is the use of a "choice" construct, or implicitness construct. When such a construct is used, the system must somehow provide an object fulfilling the requirements, if there exists one, or else issue an exception.

Exactly how an object is chosen should be completely human-controllable, but not necessarily human-controlled. There is no decidable algorithm to fulfill any constraints, but some domains are easy to deal with, and in any case, something can be inferred about the object. This human-assisted control is done through annotations and contextual information, which would be the usual means; this information could be statically or dynamically bound, and presumably with more degrees of freedom. Obviously this kind of freedom would be stripped or assumed to be impossible to optimize a run-time process.

This is how the user interface is implemented: when a program needs some data to be provided, it calls the choice axiom as part of its inference. An object is created with only the annotation that it satisfies the need and that it exists. An inference system then works out if enough information exists to infer enough about the object so that the system need not ask the user for the remainder. Obviously, if this fails, a request is posed to the user for the missing information. The user provides some information, tells the system that they have given enough, and the system proceeds to work out what has been provided and whether that is enough or even consistent, at which point the dialog may continue or the initiating process could take the given object and proceed beyond the dialog.

One way to understand programming with epsilons (Epsilon stands for Hilbert's epsilon symbol for a witness for an existential predicate) is to view them as goals in the classical paradigm of higher-order logic programming (see languages like Prolog, Mercury, Lambda Prolog, Goedel, Maude); an epsilon is a hole in the well-founded construction of a computable object; the system thus uses tactics to fill the hole; these tactics are made of rules that may include holes themselves, hence generating filling of the subholes as subgoals.

The main differences of Tunes epsilon calculus with classical logic programming is that tactics can be dynamically defined from existing tactic constructs and libraries, and they are first-class objects, themselves subject to manipulation, etc. Also, there is a guaranteed constraint that should they succeed, they should return an object that rigorously matches the requested specification; hence, whereas Prolog programs only have a global semantics that can only be deduced from the totality of rules, Tunes tactics can have a nice local semantics with local definitions.

If O is an object with a free variable e, then O[E/e] where E is a compliant tactic to fill 'hole' e is a valid object and specialization for O. E may dynamically generate holes itself, etc.

The tactics are constrained to return a result that respect the specification for the requested object, instead of being pure arbitrary tactics in a program with global semantics only.

Abstraction & Migration

Is this an example of abstraction: taking an integer addition function and abstracting it to work with floats?

If the addition function is used in a context such that the fact that the numbers are integers rather than float does not intervene, then it is a valid to abstract the number type into something more general than integers. You reapply the said abstracted object to the float number type, but the result is not the abstraction itself; If the overall abstract semantics of the new object are the same as those of the original one (i.e. you were doing computations on integers, and are now using the FPU to speed them up), then it is what I called a migration of the original object.

References considered Harmful

It seems to me that references are not a good concept in a distributed system. Communication Channels, Linear objects, and projects seem to do more than references, in more precise ways:

For shared, evolving, information, the notion of monotonically evolving projects seems interesting: projects are a special kind of "objects" (only reified at a higher-level of abstraction than usual code), that are repository for information. Such projects evolve, for divergent and convergent versions; several may be merged into one; one may be split into several. Split parts may be recombined with other projects, etc.

All in all, references look like a very low-level concept that may be rid of in most (which?) cases.

To Do:

Detail the rules to prove programs.

Find out how multiple type systems can be made compatible.

Separate annotation mechanisms from annotation resolving policies.

Find out how to separate or merge the "useful/administrative" or "in context/out of context" information. This would be a protocol for context meta-objects.

If minimal cost of emulating one "reflective" operation on a non-reflective language is a*l+b (where l is length of history) then minimal cost of n reflective operations is a^n+...+n*b. And that's best-case behavior. Even in the worst case, reflective wins.