CLIP some more!
btanksley@hifn.com
btanksley@hifn.com
Mon, 13 Mar 2000 15:08:24 -0800
I'm going to split this message into two parts: in the first part goes
anything about the applicative CLIP system, and in the second part goes
stuff about Joy.
>From: iepos@tunes.org [mailto:iepos@tunes.org]
>> > put (stringify (square 129))
>> Well... Yes. That's a procedure which _prints_ the square of 129.
>> A procedure which simply _computed_ the square of 129 would
>> likely look a lot like a function which did the same thing --
>Ahh... I'm not sure that my system has an analogue of a procedure that
>simply "computes" the square of 129... Perhaps you could use
>"T (square 129)", which would push the square of 129 onto "the stack";
>however, it is not guaranteed to be kept in the system's
>evaluated form...
I don't see how you could reliably prevent your user from writing a
procedure which computed the square of an input number. Squaring can
certainly be expressed in a procedural manner (for example, as a duplication
followed by a multiplication, or by a series of duplications followed by
addition).
Not that one would WANT to do that -- but it's easy to do. For other
things, people will want procedures. And it's mainly the other things we're
talking about here -- squaring is merely a diversion.
>Such a procedurized "square" actually can be constructed from the
>ordinary "square", specifically, as "CB square"; if you told
Interesting!
>By the way, after some thought, I've come to like Joy-styled systems
>more than before... there is one odd thing about my system that
>bothers me: I'd planned on frequently using functions like "square"
>(that do not take continuation parameters); however, the question
>arises of how to deal with similar (non-procedural) functions that
>have multiple results... for instance, how might I formalize a
>"divmod" function, that takes a couple numbers and yields both
>the result of their division and the remainder?
>The best way I can think of to formalize it is as a function
>that takes three parameters: a "continuation" function (which the
>two results will be applied to), and the two numbers. That is,
>for example, "divmod foo 3 13" would yield "foo 4 1" (since
>"3" goes into "13" four times with one leftover). Note that
>"foo" does not need to be a procedure; it just has to be some
>binary function... for example, "divmod add" (where "add" is not
>a procedure, but just a binary function that takes two numbers
>and yields their sum) would be a function that takes two numbers
>and yields the sum of their quotient and remainder.
Now this is a confusing and interesting problem. My personal feeling is
that it's wiser to restrict functions to returning exactly one value; that
single value may be a tuple.
>Of course, it is possible to formalize single-resulting
>functions using a continuation parameter, as was done above with
>"square"; this seems to me to be an interesting approach.
>If all functions took a continuation parameter, then the system
>would be brought even closer to Joy.
True, because then functions and procedures would be unified. There would
be strong logic to this -- however, it also becomes more complicated than
your original system to write and understand. But I suppose that's
irresistable; your original language (or rather, my original understanding
of your language) appears to have been oversimplified, since it had all of
those special cases.
Here's another idea: write a language which contains two distinct parts: one
mapping language and one procedural language. The mapping language is your
original functional language; the procedural language is some ideal
concatenative language.
This notion allows users all of the freedom and constructiveness of
Joy/concatenation, while giving them all of the strict typechecking and
parsing of the explicit application as well.
>Hmm, but, if you take two regular functions and compose them,
>guess what? the result is always a regular function. And, if
>you take "T" and apply to something, the result is always a
>regular function. So... how about we give up unrestrained
>application, and take composition and application of "T" as
>primitive operations. If this is done, then we'd end up with
>something very much like Joy.
Odd! That's an interesting result.
>Actually, quotation (application of "T")
>would not behave quite the same way as in the original Joy system;
>it would behave in a much better way, I think, a way which would
>allow unrestained substitution of equivalent expressions even
>within quotation;
Could you at least acknowledge my VIOLENT disagreement with your dogmatic
opinion on that subject? You seem to strongly believe that removing an
essential feature (code reflection) to fix an imaginary problem (the ability
of broken programs to reflect on code) is "much better". You have yet to
explain why allowing broken programs to run as expected is better than
allowing correctly written programs to reason about quotations.
>yet, this comes with some pitfalls,
>which I'll mention later...
Okay, I'm calm again. Sorry.
>> > Note that it is not "someFunction" itself
>> > that is executed (it would not make sense to execute a function).
>> > Rather it is the application of "someFunction" to the "foo" input;
>> Exactly. Application happens implicitly and at runtime;
>> everywhere else in
>> your system, application is obvious, explicit, and
>> compile-time. Do you still not see the asymmetry?
>Maybe, but it does not seem to be a problem to me.
I have to agree with that -- it's not a problem. It's at worst an
inconsistency. And some of the modifications you've made remove it as an
issue.
>It seems analagous to what happens when one uses a Joy primitive
>like "get", which pushes a string onto the stack; usually,
>things are pushed onto the stack explicitly; does it seem
>odd that "get" pushes something onto the stack "implicitly"?
Um... Almost everything is pushed onto the stack implicitly. Explicit
stack pushes are VERY rare; you could imagine that only a literal is an
explicit stack push.
OTOH, all application is explicit in your language -- except for procedures
and combinators. Of course, combinators are essential, so we don't want to
get rid of them.
Now you've fixed the last vestige of the problem, IMO, since you've changed
procedures to act more like combinators. Now instead of a pair of
exceptions, you have a single rule.
>I see your point... This is mostly just a funny feature of English,
>though, of course; in a formal language, there would be no confusion
>in mentioning "Bill" twice. I think my later example with the
>keyboard lights may have better illustrated my original point
>(that there is use in talking about abstract physical things
>rather than specific states of such things, as in the num-lock light
>on my keyboard).
Yes, I addressed that point briefly. The problem with it is that you assume
that name-based functional and object oriented styles can be combined
seamlessly and without effort on your part. This assumption has been proven
false so many times in so many different languages it's a little scary.
There's one language I've seen which unifies both ways of thinking, but I've
totally forgotten its URL. Tim Peters, Python Guru, claims that Mozart/Oz
has also managed to unify the styles, so perhaps it would be worth your
while to examine. I wasn't very impressed with it, but Tim knows a lot
better than I do.
>> > If I understand, you want your "Rinse" procedure to take a dish,
>> > and then result in the new dish (the new "rinsed" dish, as if
>> > it were distinct from the original; I guess we are now talking
>> > about the states of the dishes, so to speak, rather than the
>> > "abstract identity" of the dishes).
>> Correct. This is neccessary in order to model the behavior
>> of the procedure in a functional manner.
>Ahh... I see... It is possible to view your "Rinse" procedure as
>a pure procedure, which has no effect (except on the stack, of course),
>while the same is not true of mine.
Yes. It's also possible to view it as a pure function.
>Types... this reminds me of the interesting type system that the
>author of Joy (Manfred von Thun) developed...
>This type system doesn't give a way to express the purity of
>the procedures, but it could be easily extended to do so, I think...
Hmm. Yes, that makes sense. I can see how that would be very convenient
for the optimizer.
>There is an alternative to "->"... "->" could be eliminated, I think,
>if one had instead a unary type constructor, which I'll call "-", which
>takes a type, and yields the "opposite" type (if a type pushes
>something onto the stack, then the opposite type will pop something
>off the stack, and vice-versa)... Then, the type of "+" could be
>written
> -Int -Int Int
>Anyway, this is tentative... I haven't really thought too much
>about using "-", or about type systems at all...
It's an interesting suggestion. It makes memory management a little easier,
possibly, since it states explicitly how the function takes things off the
stack. Stack depth would be a lot easier to check.
>> So, we start out with a function, which represents a
>> procedure. Every
>> procedure requires at least one parameter to be bound to it: its
>> continuation parameter. The function takes as an argument a
>> continuation,
>> and returns a completion.
>The continuation taken may sometimes be a command (if the procedure
>has no results), or it may be a function onto a command (if
>the procedure has some results). In our new terminology,
>a function (possibly of many arguments, through currying) onto
>a command
>is a "completion"; so, "continuation" is synonymous with "completion"
>and a procedure is just a function from a completion to a completion.
Interesting point. Yes, that makes sense. Great!
>> Yes. That works very well -- except, of course, for the
>> ability to treat
>> that program as a list of operations.
>Ahh... how about this: a program as a bunch of opcodes on the stack,
>followed by an integer telling how many there are. Of course, this
>would mean that when you deal with quoted programs, it is not
>enough to juggle one stack item; you have to juggle lots of them
>(except in the case of the empty program)... that's okay though;
>I'm not sure about this ... dumping lots of things onto the stack
>instead of dumping just a "list" of such things. I'm pretty sure
>that if I implemented a program to push a "list" of things
>onto the stack, I would implement it by having it just dump all
>of its elements... but maybe there should be a distinction at a
>high level (between a program that pushes a "list" of things and
>a program that just pushes all the things)...
>What do you think... ?
I know which one I would prefer to work with -- the unified list, all the
way. There's just no way I'd want to deal with a whole bunch of
variable-stack-effect words.
>- "iepos" (Brent Kerby)
-Billy