Surprised by Joy
Massimo Dentico
m.dentico@teseo.it
Sun, 12 Mar 2000 03:52:06 +0100
btanksley@hifn.com wrote:
>
> [...]
>
> >From: iepos@tunes.org [mailto:iepos@tunes.org]
>
> >It would be possible to make types first-class... a type could
> >be thought of as a pure procedure which takes a thing off the
> >stack, and pushes a proposition (True or False) indicating
> >whether the popped thing was a member or not. But, maybe this would
> >not be a good idea... I'm not sure...
>
> RTTI, in other words.
Not necessarily Run-Time Type Information. You can utilize this
information (when possible) at compile-time or in any stage in a
multi-stage programming environment.
> >Type systems are usually
> >kept separate from the rest of the system.
>
> Ah, but the type information is useful and shouldn't be kept hidden. In
> addition, TUNES has this "thing" about wanting everything to be first class.
In this regard, Cayenne and MetaML are 2 languages from which we
could lead inspiration:
Cayenne is a language with first class types combined with
dependent types; it also unify the syntax of expressions and
definitions for values, types and modules.
- http://www.md.chalmers.se/~augustss/cayenne/
MetaML is a language with explicit staging annotations useful for
building program generators; the notion of type-safety in MetaML
is "strong": meta-programs and object-programs (programs
manipulated by the meta-programs) are guaranteed to be free of
run-time errors.
- http://www.cse.ogi.edu/PacSoft/projects/metaml/index.html
> >And, mentioning "-" reminded me of something else... One interesting
> >primitive one could add to the system is an "undo" primitive...
> >Such a primitive would take a quoted program off the stack, and
> >would "undo" the quoted program. This probably seems like a
> >wild idea; "undo" is usually thought of as a fancy feature often added
> >to text editors and things... to treat it as a fundamental primitive
> >of a system is getting weird... it would probably make the system
> >impossible to fully implement.
>
> Probably. However, at the same time, it _is_ possible; existance proof is
> in the language 'J', which implements an adverb 'power'. n-th power repeats
> the given verb n times, but if n is negative is repeats the inverse of the
> verb n times.
Another language with this feature is Prolog. Moreover is known
that Supercompilation, a program transformation technique (more
powerful than Partial Evaluation) elaborated by the Russian
scientist V. F. Turchin in the early seventies, can be used to
invert a computation. A very interesting theory (some times cited
by Faré) developed by Turchin is MetaSystem Transition (MST); in
the context of Computer Science, it concerns meta-computation and
self-application. Here a reference:
"A Roadmap to Metacomputation by Supercompilation", R. Glück, M.
H. Sørensen:
- http://www.diku.dk/research-groups/topps/bibliography/1996.html#D-267
There is also a paper of Henry Baker that deals with the reversal
of computation, "NREVERSAL of Fortune - The Thermodynamics of
Garbage Collection":
- ftp://ftp.netcom.com/pub/hb/hbaker/ReverseGC.html
- ftp://ftp.netcom.com/pub/hb/hbaker/ReverseGC.ps.Z
> >But, there are interesting things about "undo"... It seems to be
> >the analogue of the "inverter" primitive that I wanted in my
> >applicative system. Using it in conjunction with the Church Numbers
> >(those "doonce", "dotwice", "dothrice", and such, that I mentioned
> >earlier), it would be possible to form all kinds of interesting
> >Church numbers (including negative, fractional, irrational, imaginary,
> >etc.). Actually, "undo" might itself be considered the Church Number
> >for negative one.
>
> Hmm. Possibly... Yes, that makes sense.
>
> >Then, as an example, one could form a "dohalf" primitive, that
> >takes a quoted program and executes it a "half" a time:
>
> > [[dotwice] cons] undo
>
> I don't see that one. It looks to me like -2, not 1/2, since it undoes a
> function twice. A simpler way to express -2 would be:
>
> [dotwice] undo.
>
> Our functions aren't identical in their effects, though; perhaps I'm wrong.
> Your function actually looks more like it'll undo a cons with dotwice, which
> is very different from undoing something twice.
>
> >I think that is right... and, the "dotwice", "dothrice", and such
> >I used earlier took a quotation and resulted in a quotation;
> >as I use them now, they do not result in a quotation, but rather
> >just execute the quotation they are given so many times...
>
> Right. Fortunately, it's trivial to use quotation to allow them to result
> in a quotation.
>
> >would be a function that subtracts 5... I guess then that
>
> > [add] cons undo
>
> >would be a subtraction program...
>
> It would be a program which undid adding something. I don't see why you
> can't do
>
> [add] undo.
>
> , though.
>
> >And, of course, some programs do not make sense to be run a negative
> >number of times, or a fractional or imaginary number of times
> >(although some do), or some might make sense in multiple ways...
> >Not sure how a system would deal with this...
>
> Nor am I. J deals with it by only implementing power partially: only
> specific primitives, and specific ways of combining those primitives, have
> inverses.
>
> >Now seems like a good time to share the idea I had about altering
> >the quotation construct of Joy to make it better correspond to
> >application of "T"... (I don't claim that this enhancement
> >would make Joy better; in fact, looking back, I think it would make
> >it worse, although it does inspire a few other thoughts)
>
> Thought is good. :)
>
> >The basic idea is to make "[foo]" denote the program of pushing
> >the program "foo" onto the stack; then, for instance, "[add]" and
> >"[swap add]" are equivalent programs, since each pushes the "adding"
> >program onto the stack. And... if you told the mythical system to
> >do "[add] [swap add] =", it would push "True" onto the stack
> >(although it might not realize it, if it is acting in a lazy style).
>
> Okay. As far as this goes, that's great -- but it's not possible in general
> to test the equality of programs. I would recommend that '=' treat programs
> as incomparable unless they're actually the same program. Now, it's
> possible for ==> and => to test the equality of programs under a finite set
> of transforms, and in such a treatment, given the commutative axiom, your
> two programs would result in True.
>
> >To emphasize, in my mythical modified Joy system, "[add]" and
> >"[swap add]"
> >mean the same thing to the system and can be replaced by each other.
> >A consequence of doing it this way is that programs cannot directly
> >be introspected; however, this could be fixed by adding a "reify"
> >primitive.
A paper about a reification primitive: "The next 700 reflective
object-oriented languages"
- http://www.emn.fr/cs/object/biblio/abstracts.html#ds99a
or directly here:
- http://www.emn.fr/info/recherche/publications/RR99/99-1-INFO.ps.gz
> This would indeed be a consequence (although adding a reification primitive
> would not solve it, but rather push off the real solution to another level).
>
> I think a better solution is to provide two new types and two new operators.
>
> The first type is 'symbol'; an object of this type may or may not represent
> a function in this or some other context.
>
> The second type is 'function'; an example of the function type is
> demonstrated by running
>
> [+ +] uncons
>
> The result of this is a function followed by a list (which contains a
> function).
>
> The first new operator is "=>"; it acts like Joy's notation, but returns
> true if and only if one of its first argument can be rewritten as its second
> argument using a single rule.
>
> The second new operator is "==>"; like the first, it checks whether its
> first argument can be rewritten to look like its second with any number of
> steps.
>
> (Note that these two operators may not always be commutative, depending on
> what rules the system knows. Other operators could be provided which were
> commutative, such as "<==>".)
>
> These four additions allow reasoning about quotations even though functions
> cannot be compared for true equality. In addition, the new datatypes make
> it possible to write functions which expect 'quotations' in a specific
> format.
>
> For example, you recall my [0 add] example, where my program expected to be
> able to uncons the input, but the optimizer had optimised the "0 add" away.
> Under my new system, the input would be better expressed as something like
> [0 #add], where #name returns a symbol.
>
> >Now, there is the issue of lists... how will lists be represented,
> >now that quoted programs no longer have introspection?
>
> I recommend that quoted programs keep their full introspective capabilities.
>
> >My answer
> >is that you do not need lists; just push all the elements directly
> >onto the stack (perhaps with an additional integer, indicating
> >how many there are). Then, make all the "list" operators
> >(such as "reverse", "concat", and "cons") operate on the bare
> >stack.
>
> You know what? I really dislike this idea. It seems so messy! If you
> _really_ felt it to be important that lists not be the same as quotations (I
> don't think that's important, but to each his own I suppose), simply create
> a new datatype named 'list'.
>
> >By the way, if for some reason you really needed to manipulate
> >all the elements as a single beast, then you could use a quoted program
> >that pushes the elements onto the stack.
>
> Yes, but without the ability to do quote introspection there's no way to
> build such a list at runtime.
>
> I don't think this is a good idea. However, it does point out a good idea
> -- right now Joy has an "unquote" combinator. I think we also need an
> "unlist" function, which dumps everything in a list (or quotation) on the
> stack without executing any of it.
In a paper that deal with MST and staging, I have found this
criticism about the quote/"unquote" mechanism:
"Reasoning about Hierarchies of Online Program Specialization
Systems", J. Hatcliff, R. Glück
- http://www.diku.dk/research-groups/topps/bibliography/1996.html#D-269
=================================================================
[...]
Quote/Backquote Mechanism:
The advantages of using the quote/backquote mechanism include low
space consumption, and fast encoding and decoding (simply adding/
removing a quote around an S-expression). The time to encode/
decode is constant and independent of the number of levels. The
disadvantage of is that changing the degree of a single sub-
expression may require a non-local modification of the data
structure, as shown above. Moreover, one cannot reason locally
about a subexpression because the encoding is relative, that is,
the degree of a subexpression depends on the degree of the
enclosing expression. The quote/backquote mechanism is an
appropriate solution if a monotone, relative, and non-
compositional encoding is sufficient. However, this is generally
not the case in metasystem hierarchies.
[...]
=================================================================
Immediately after, they present a technique based on level index-
ing to avoid these shortcomings.
> >By the way, this talk of pushing lots of stuff onto the stack reminded
> >me of Henry Baker's "The Forth Shall be First", which I was reading
> >again a bit ago... he really seems to advocate "linear"
> >implementations,
>
> You know, great minds think alike ;-). The moment I read about Joy, linear
> logic popped to mind.
>
> However, the really interesting thing about Joy-type systems is that I
> suspect that it's possible to _mix_ linear logic with real GC. The trick is
> that some functions, such as deepcopy, are "linear", and others, such as
> "dup", are not. We could use a generational garbage collector, where the
> first 'generation' is managed by linear logic, and objects are tagged to be
> managed otherwise simply by having a non-linear operator used on them.
>
> So in a naive system, the definition "myoperator == dup drop" is nonlinear.
> (Of course, in a more sophisticated system this causes no problem; it's
> clearly linear.)
>
> >Also, there would need to be some way of threads coordinating with
> >each other... in my system, I had thought of using Fields...
> >new fields could be constructed with a "new" primitive, and then
> >they coulded be fetched or stored using "get" and "set" primitives
> >(like the "@" and "!" of FORTH)... multiple threads could use
> >the same fields... however, I'm not convinced that this is the
> >best way; it actually seems to be not a very good way at all,
> >considering the synchronization issues. In a linear system, it may
> >also tend to cause wasteful moving around of data structures
> >(from a thread's stack, to a field, and then to another
> >thread's stack).
>
> A facinating question. I've put no time into considering it before.
>
> Multithreading (without the capability of message-passing) is possible
> simply by saving the data stack pointer, return stack pointer, and
> instruction pointer (and not sharing stacks). The easiest way for me to
> imagine message-passing is via a send/recieve pair of primitives; I suspect
> that both block until the transaction is complete (this also implements
> synchonization). If you need asynchronous messaging, you really need an
> extra thrad anyhow, and our library will provide a stock way to set one up
> (but it likely won't be a primitive).
Michael Montvelishsky has added the primitive for the concurrent
and parallel programming of Occam and Linda to Forth (a simple
task because Forth has powerful extension capability). They
represent 2 extremes among the models of concurrent computations.
Occam has named channels with blocking (synchronous without
buffers) communication primitives, so that two processes that
want to communicate must exist contemporarly and to know the name
of the channel (a process that want to send/receive on a channel
is blocked until the counterpart is ready); it is the most
efficient solution.
Linda instead realizes a complete decoupling in time and space: a
process must not know the name of a channel or of another process
but simply there are active tuples (AT) that consumes passive
tuples (PT) (a tuple is like a record in wich the 1st field is a
string), so there isn't a global scope in wich are defined the
communicating processes like in Occam or other models. A process
that need a service put a PT, with parameters and the name of the
service (1st field), in the Tuple Space (TS) then it can
immediatly continue. A process that provides a service put an AT
in the TS and eventually process a PT that match his AP (however,
there are 2 versions of the primitives to read (put an AT) and
write (put a PT) from the TS: blocking and non-blocking). This
model is not so efficient like the Occam model, but it is very
flexible, allows a dynamic use of resources and automatic load
balancing (ATs "grows thick and multiplies" around the PTs).
You could find these extensions on the site of Ultratechnology:
"PARALLEL FORTH: The new approach" (Occam)
- http://www.ultratechnology.com/4thpar.html
"Forth-Linda"
- http://www.ultratechnology.com/4thlinda.html
There is also a "Distributed Shared Memory in Forth":
- http://www.ultratechnology.com/4thdsm.html
However, these are explicit methods to deal with concurrent/
parallel computations. Implicit methods also exists, they
exploits the inherent parallelism in programs.
> >Another option is to have some direct primitives by which one
> >can transfer items from one thread's stack to another... one
> >would need to have been explicitly set to read and the other
> >explicitly set to write (so that items would never get pushed
> >onto a thread's stack by another while it was in the middle of doing
> >something else)...
>
> Whoops, I see you were thinking of that. I prefer this solution; it's more
> directly implementable (more primitive).
Surely. These primitives are similar to the Occam primitives for
synchronous communication.
The analogy here (I'm not sure to what extent) is between
channels and stacks. They are simple enough to be implemented in
hardware (Inmos Transputer processors). Moreover, Occam takes
these primitives (and other like SEQ and PAR to specify
sequential and parallel computations) from CSP (Communicating
Sequential Processes) by C. A. R. Hoare, a framework to formally
study concurrent programming (see the homonym book "CSP", by
Prentice Hall International; there is an italian translation
"CSP - Processi Sequenziali Comunicanti", by Gruppo Editoriale
Jackson).
> You mentioned an overhead due to linear logic; however, may I point out that
> in this case there _is_ no overhead to linear logic, because each item is
> referred to by exactly one pointer, and when we transfer the item to another
> thread we're actually transferring only the pointer (and the right to use
> it, of course). If any copying actually happens, it would have happened
> anyhow.
>
> >Anyway, this is just another tentative idea...
> >By the way, in a nice system, I would probably still like to
> >have fields... they just don't seem to be good low-level primitives
> >for an implementation... fields could probably be constructed
> >using this stack-transfering primitive, by making a thread for
> >each field, a thread that has the field's value on its stack,
> >and which waits for some signal (to fetch or store), at which
> >point it would either read in a replacement value for the
> >stack or else write a copy to some other thread...
>
> Hmm. You make a good argument for making 'fields' be a datatype in their
> own right. In most architectures a primitive implementation of 'field'
> would be _far_ more efficient (and simpler to implement) than this.
> However, this is an _excellent_ model of how fields operate mathematically:
> they essentially _are_ a seperate process.
There are other mathematical models of concurrent computation
besides CSP, for example CCS (Calculus of Communicating Systems)
of R. Milner or the Event Calculus that P. J. Knaggs uses in his
thesis "Pratical and Theoretical Aspects of Forth Software
Development" - chapter 8 "The Event Calculus" - p. 63.
- http://dec.bournemouth.ac.uk/dec_ind/pknaggs/thesis/index.html
> >> Okay. I'm dizzy. ;-)
>
> >> A procedure is a function which takes a continuation
> >> parameter, and returns
> >> a function (if it can take more parameters) or a command (if
> >> that's all the parameters it can take).
>
> >Exactly. Ahh... we're talking again about my applicative system...
> >I'm still interested in it, although I'm now more interested
> >in implementing a refinement of the Joy system than in implementing
> >that purely applicative system... In a bit I'll mention some
> >refinements I have in mind...
>
> Wow. Cool. :-)
>
> Yes, refinements are _certainly_ needed.
I have taken an interest into a development of this sort too.
--
Massimo Dentico