Re: [stack] three more proposals about Joy
Louis Madon — 2000-07-01 04:49:59
iepos@... wrote:
>
> Hello folks ...
>
> The last while, I've been thinking a bit about a few ways I would like
> Joy to be modified. You may or may not like these ideas ...
>
> 1) Design the system so that the user feels free to pile tons of items
> onto the stack (treating it as an array). Modify the "size"
> primitive
> to tell how many items are on the stack. Add a primitive "wrap"
> that wraps the whole stack up into a list. Make it feasible to
> use "call" ("i") to unwrap a list onto the stack. Modify "map" to
> apply a quotation directly to all the other items on the stack,
> instead of taking a list; thus, one could do
>
> 1 2 3 4 [dup *] map
>
> and get
>
> 1 4 9 16
>
> Modify several other list-oriented primitives ("first", "rest",
> "at",
> "take", and others) to take things directly off the stack instead
> of
> from lists.
>
> Rationale: it is simpler; it eliminates the need for many "[]"s and
> related operators which serve no purpose.
Would it be possible to see some before/after examples illustrating what
you mean by features not serving any purpose?
A basic principle of the stack is to provide a place to store/retrieve
intermediate values as the system traverses the program function
hierarchy. The key is ensuring that each function consumes only those
values it really needs and returns results without disturbing any
unconsumed values. The important word here is "value" - how can a
function know what to consume if some values actually occupy multiple
places (as in an unaggregated list)?
If I understand your proposal correctly this retrieve/store semantics
must now be done explicitly (whenever lists are involved) by using
call/wrap. For example, say I want a function that can be given a list
of numbers to compute their average and median:
In Joy, I could do:
dup average swap median
However, in this proposal, it seems I would have to do something like:
wrap dup [call median] dip swap [call average] dip
unless I'm way off base, this is not simpler ...
--
Louis.
Louis Madon — 2000-07-01 07:45:13
iepos@... wrote:
> 3) Replace Joy's "==" construct, and its friends, "LIBRA", "HIDE",
> "IN",
> and "END" with a fully-fledged lambda construct. (Okay, I've
> already
> mentioned this before).
>
> Basically, a lambda in my notation looks like "foo\" where "foo"
> is any name, hitherto undefined, that the user wants to use.
> "foo\" can be thought of as a command that means,
>
> Pop the top item off the stack and call it "foo".
>
> Thus, if a user has been fiddling with the interactive interpreter,
> and has ended up with an interesting list or other thing on top
> of the stack, so interesting that he wants to give it a name, he
> can say "foo\", and *INSTANTLY* the system will bind the name "foo"
> to the item that was on the stack (which is now popped off).
> If the user becomes tired of that name, he can end the lambda's
> scope by doing "\foo".
This could be useful. A while back during the discussion of a
topological sort function I wrote both a Haskell and Joy version of that
function:
-- Haskell
topsort [] = []
topsort xs =
let (es, xs') = sepEmpties in
es ++ (topsort (map (mkDepUpd es) xs'))
where
mkDepUpd es = \(node, deps) -> (node, deps \\ es)
sepEmpties =
let (es, xs') = partition (null . snd) xs in
((fst . unzip) es, xs')
-- Joy
topsort ==
sepEmpties
[null]
[pop]
[dupd swap mkDepUpd map topsort concat]
ifte;
mkDepUpd == unitlist [unpair] swoncat [setdiff pair] concat;
sepEmpties == [second null] split [firsts] dip;
One thing I noticed was that the 'mkDepUpd' helper function was quite a
bit more complicated in the Joy version. But if I had your lambda
notation, it could be written as:
mkDepUpd == x\ [unpair x setdiff pair];
Thats quite a bit simpler. On the downside, I suspect it will make
formal reasoning harder (I guess the lambdas could be automatically
transformed to standard code though) and you have to worry about
inadvertantly shadowing names - especially if you forgot to retire a
binding with /foo.
>
> A lambda could also be used for definitions; for instance, one
> could
> define "swapd" by doing:
>
> [[swap] dip] swapd\
Is it still a compile-time feature? (ie. I hope your not suggesting
named definition is done at runtime).
--
Louis.
stevan apter — 2000-07-01 12:53:37
----- Original Message -----
From: <iepos@...>
To: <concatenative@egroups.com>
Sent: Friday, June 30, 2000 10:17 PM
Subject: [stack] three more proposals about Joy
> Hello folks ...
>
> The last while, I've been thinking a bit about a few ways I would like
> Joy to be modified. You may or may not like these ideas ...
>
> 1) Design the system so that the user feels free to pile tons of items
> onto the stack (treating it as an array). Modify the "size" primitive
> to tell how many items are on the stack. Add a primitive "wrap"
> that wraps the whole stack up into a list. Make it feasible to
> use "call" ("i") to unwrap a list onto the stack.
10 20 30 40 50 60 collect count
10 20 30 40 50 60 6
'collect' appends the enlist of the stack to the list. 'count' is
conk's size primitive.
> Modify "map" to
> apply a quotation directly to all the other items on the stack,
> instead of taking a list; thus, one could do
>
> 1 2 3 4 [dup *] map
>
> and get
>
> 1 4 9 16
1 2 3 4 stack [dup *] map unstack
1 4 9 16
>
> Modify several other list-oriented primitives ("first", "rest", "at",
> "take", and others) to take things directly off the stack instead of
> from lists.
>
> Rationale: it is simpler; it eliminates the need for many "[]"s and
> related operators which serve no purpose.
>
> 2) Eliminate the ability to do direct introspection on quotations.
> This is actually a consequence of the suggestion in #1;
> "first", "rest", "at", "take", "size" could no longer be used to
> peek into a quotation, since the reformed versions fiddle with items
> directly on the stack instead of in quotations.
in view of the availablility of stack and unstack (and in conk, collect
and disperse) do you still think this is a good idea?
>
> Anyhow, it might be useful for a system to have "reify" word that
> takes a program, and dumps a corresponding bunch of tokens onto
> the stack, or some other representation of the program. A system
> might also want to provide "compile", the complement to "reify";
> this would take a bunch of tokens and transform them into a program
> that can be manipulated with "call", "concat", "cons", and such.
see above.
>
> Rationale: allowing direct introspection of quotations seems to require
> the system to keep track of the exact way a user has constructed
> a quotation; this is bad in many circumstances. For example, imagine
> that the user has pushed "[2 + 3 +]" onto the stack. It would be
> nice if the system could internally rewrite this as "[5 +]". However,
> this would not be permitted if direct introspection is allowed,
> because the user could be relying on the quotation to stay exactly
> as he put it on there, as "[2 + 3 +]". (Okay, that wasn't exactly
> a really good example).
:)
>
> 3) Replace Joy's "==" construct, and its friends, "LIBRA", "HIDE", "IN",
> and "END" with a fully-fledged lambda construct. (Okay, I've already
> mentioned this before).
>
> Basically, a lambda in my notation looks like "foo\" where "foo"
> is any name, hitherto undefined, that the user wants to use.
> "foo\" can be thought of as a command that means,
>
> Pop the top item off the stack and call it "foo".
in conk, it's called 'set':
"foo" [2 +] set
`foo
4 foo
6
>
> Thus, if a user has been fiddling with the interactive interpreter,
> and has ended up with an interesting list or other thing on top
> of the stack, so interesting that he wants to give it a name, he
> can say "foo\", and *INSTANTLY* the system will bind the name "foo"
> to the item that was on the stack (which is now popped off).
> If the user becomes tired of that name, he can end the lambda's
> scope by doing "\foo".
i don't think we need lambdas to do that. see above.
>
> A lambda could also be used for definitions; for instance, one could
> define "swapd" by doing:
>
> [[swap] dip] swapd\
>
> However, a lambda could be used for formal variables, when the items
> to name are not statically known. For instance, one could define
> "concat" using lambdas as:
>
> [x\ y\ [y x]] concat\
>
> One could define a "squaring" program as
>
> x\ [x] [x] *
>
> Anyhow, I won't go into this anymore, since it has already been
> discussed quite a bit between Billy and I. One could scan through
> the archive of this list (and the TUNES list) for subjects containing
> "Lambda".
sounds interesting - i'll have a look.
>
> - "iepos" (Brent Kerby)
>
> ------------------------------------------------------------------------
> Visit Ancestry.com for a FREE 14-Day Trial and find your ancestors now.
> Search over 550 million names and trace your family tree today. Click here:
> http://click.egroups.com/1/5528/7/_/_/_/962417870/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
iepos@tunes.org — 2000-07-01 14:12:59
> > 1) Design the system so that the user feels free to pile tons of items
> > onto the stack (treating it as an array). Modify the "size" primitive
> > to tell how many items are on the stack. Add a primitive "wrap"
> > that wraps the whole stack up into a list. Make it feasible to
> > use "call" ("i") to unwrap a list onto the stack.
>
> 10 20 30 40 50 60 collect count
> 10 20 30 40 50 60 6
>
> 'collect' appends the enlist of the stack to the list. 'count' is
> conk's size primitive.
Yes... that would work. This "collect" is not exactly the same as "wrap";
"wrap" would _replace_ the stack with a list containing its
items, not just make a copy. Thus one could use "wrap pop" to wipe
out the whole stack. I'd prefer it this way, because I tend to like
"dup" to be the only primitive that does duplication (and similarly,
"pop" to be the only primitive that does destruction). One could
construct "collect" as
collect == wrap dup dip
One could also do the reverse, construct "wrap" from "collect", but
it would be trickier, I think. One would have to use a loop with "pop":
wrap == collect dup [count [pop] iterate] dip
(supposing "iterate" takes a program and a number and executes the program
that many times).
> > 2) Eliminate the ability to do direct introspection on quotations.
> > This is actually a consequence of the suggestion in #1;
> > "first", "rest", "at", "take", "size" could no longer be used to
> > peek into a quotation, since the reformed versions fiddle with items
> > directly on the stack instead of in quotations.
>
> in view of the availablility of stack and unstack (and in conk, collect
> and disperse) do you still think this is a good idea?
Well, I still like the idea. It seems that making "map" and others
list-oriented would just make it necessary to use "stack" and "unstack"
(or "wrap" and "call") a lot. I still like making "map" fiddle directly
with many items on the stack; it simplifies things.
Of course, I don't really have a solid argument that it would really
be simpler. This is just an idea. To be sure, one would have to
implement a real system this way and then see if it is simpler.
(I hope to get around to this someday :-)).
> > 3) Replace Joy's "==" construct, and its friends, "LIBRA", "HIDE", "IN",
> > and "END" with a fully-fledged lambda construct. (Okay, I've already
> > mentioned this before).
> >
> > Basically, a lambda in my notation looks like "foo\" where "foo"
> > is any name, hitherto undefined, that the user wants to use.
> > "foo\" can be thought of as a command that means,
> >
> > Pop the top item off the stack and call it "foo".
>
> in conk, it's called 'set':
>
> "foo" [2 +] set
That's fine. I'm guessing that this "set" is not designed to use for
formal variables. That is, you wouldn't want to do something like this:
concat == "x" swap set "y" swap set [y x] "y" unset "x" unset
- "iepos" (Brent Kerby)
stevan apter — 2000-07-01 14:34:43
if you think of the stack as a list, then it makes sense to
add list-programming primitives to joy. my approach has been
to turn k, where the list primitives already exist, into a
concatenative stack language. (k without lambdas, that is)
in conk, stack and unstack, and collect and disperse, are
used to bring the stack into range for list-processing.
for example
1 2 3 4 stack dup * unstack
1 4 9 16
i.e.
1 2 3 4
1 2 3 4
stack
[1 2 3 4]
dup
[1 2 3 4][1 2 3 4]
*
[1 4 9 16]
unstack
1 4 9 16
* knows how to multiply (conformable) lists.
in fact, stack/unstack and collect/disperse are so useful
that i've given them symbols:
1 2 3 4 ( dup * )
1 4 9 16
1 2 3 4 (( dup * ))
1 2 3 4 1 4 9 16
in addition to atomic functions like + and * (functions which
penetrate lists to apply to the atoms) there are pure list
functions. some of these are familiar, like count (size).
others are less familar, but have proven over the course of
35 years (yes, APL has been around that long!) to be useful
computational building-blocks.
for example, consider the problem of selecting items of a list
which satisfy some condition. you could use filter, but as we
shall see, the k approach is more general:
20 10 draw (* 10 random numbers between 0 and 19 *)
[8 7 17 17 6 14 9 9 9 8]
dup
[8 7 17 17 6 14 9 9 9 8] [8 7 17 17 6 14 9 9 9 8]
10
[8 7 17 17 6 14 9 9 9 8] [8 7 17 17 6 14 9 9 9 8] 10
< (* nb: 10 < x, < is atomic *)
[8 7 17 17 6 14 9 9 9 8] [0 0 1 1 0 1 0 0 0 0]
where (* applied to a boolean vector, return true indices *)
[8 7 17 17 6 14 9 9 9 8] [2 3 5]
swap
[2 3 5] [8 7 17 17 6 14 9 9 9 8]
at (* use the indices to select from the list *)
[17 17 14]
i.e. select takes a list and a boolean function of a list and returns a list:
select == [dup] dip i where swap at (* L B -> M *)
20 10 draw
[14 8 15 12 14 18 5 17 13 10]
[10 >] select
8 5
the k approach is more general than filter because 'where' applies to integer
vectors, not just booleans:
[0 1 2 3 4 5] where
1 2 2 3 3 3 4 4 4 4 5 5 5 5 5
here's another example, which corresponds roughly to sql's 'group by' clause.
5 10 draw
[2 3 0 0 1 1 4 4 2 1]
dup group
[2 3 0 0 1 1 4 4 2 1] [[0 8] [1] [2 3] [4 5 9] [6 7]]
group takes a list L and returns a list of lists M. the first list in M
is the indices of the first item of L; the second list in M is the indices
of the next unique item of L; etc. so we can see that 2 occurs in L at
indices 0 and 8; 3 at 1; &c.
to get the unique items of L:
dup [first] each
[2 3 0 0 1 1 4 4 2 1] [[0 8] [1] [2 3] [4 5 9] [6 7]] [0 1 2 4 6]
(nb: the unique primitive is faster)
to get the counts:
pop dup [count] each
[2 3 0 0 1 1 4 4 2 1] [[0 8] [1] [2 3] [4 5 9] [6 7]] [2 1 2 3 2]
to sum each group in the list:
swap at
[[2 2] [3] [0 0] [1 1 1] [4 4]]
[0 [+] over] each
[4 3 0 3 8]
notice that at-indexing takes a list L and a list of lists M, and penetrates L with
M s.t. the result is as nested as the product of the nestings of L and M. this is
very powerful.
the second fragment applies the program [0 [+] over] to each item of the list of
lists gotten through indexing. the program [0 [+] over] starts with 0, and applies
[+] repeatedly on its own result - intuitively, the operation of summing.
in k, we would write this as:
+/'x@=x
i.e., add over each x indexed by the group of x.
as you can see, the k expression is quite a bit more direct. part of my work with
conk is to gauge the further cognitive burden stack programming places on the
programmer. (this is my first venture into that way of thinking.) at some point, i hope to have defined a set of sql
building-blocks: tables are (roughly) dictionaries of equal-length lists, &c.
stevan apter — 2000-07-01 15:02:03
----- Original Message -----
From: <iepos@...>
To: <concatenative@egroups.com>
Sent: Saturday, July 01, 2000 10:12 AM
Subject: Re: [stack] three more proposals about Joy
> > > 1) Design the system so that the user feels free to pile tons of items
> > > onto the stack (treating it as an array). Modify the "size" primitive
> > > to tell how many items are on the stack. Add a primitive "wrap"
> > > that wraps the whole stack up into a list. Make it feasible to
> > > use "call" ("i") to unwrap a list onto the stack.
> >
> > 10 20 30 40 50 60 collect count
> > 10 20 30 40 50 60 6
> >
> > 'collect' appends the enlist of the stack to the list. 'count' is
> > conk's size primitive.
>
> Yes... that would work. This "collect" is not exactly the same as "wrap";
> "wrap" would _replace_ the stack with a list containing its
> items, not just make a copy.
that's called 'stack', in both conk and joy. unstack replaces the
stack with the top-most list on the stack. newstack is [] unstack.
> Thus one could use "wrap pop" to wipe
> out the whole stack. I'd prefer it this way, because I tend to like
> "dup" to be the only primitive that does duplication (and similarly,
> "pop" to be the only primitive that does destruction). One could
> construct "collect" as
>
> collect == wrap dup dip
>
> One could also do the reverse, construct "wrap" from "collect", but
> it would be trickier, I think. One would have to use a loop with "pop":
>
> wrap == collect dup [count [pop] iterate] dip
>
> (supposing "iterate" takes a program and a number and executes the program
> that many times).
>
> > > 2) Eliminate the ability to do direct introspection on quotations.
> > > This is actually a consequence of the suggestion in #1;
> > > "first", "rest", "at", "take", "size" could no longer be used to
> > > peek into a quotation, since the reformed versions fiddle with items
> > > directly on the stack instead of in quotations.
> >
> > in view of the availablility of stack and unstack (and in conk, collect
> > and disperse) do you still think this is a good idea?
>
> Well, I still like the idea. It seems that making "map" and others
> list-oriented would just make it necessary to use "stack" and "unstack"
> (or "wrap" and "call") a lot. I still like making "map" fiddle directly
> with many items on the stack; it simplifies things.
if the stack was ... A B L P then how would you apply P to items of the list L?
your proposal makes map too strong.
>
> Of course, I don't really have a solid argument that it would really
> be simpler. This is just an idea. To be sure, one would have to
> implement a real system this way and then see if it is simpler.
> (I hope to get around to this someday :-)).
>
> > > 3) Replace Joy's "==" construct, and its friends, "LIBRA", "HIDE", "IN",
> > > and "END" with a fully-fledged lambda construct. (Okay, I've already
> > > mentioned this before).
> > >
> > > Basically, a lambda in my notation looks like "foo\" where "foo"
> > > is any name, hitherto undefined, that the user wants to use.
> > > "foo\" can be thought of as a command that means,
> > >
> > > Pop the top item off the stack and call it "foo".
> >
> > in conk, it's called 'set':
> >
> > "foo" [2 +] set
>
> That's fine. I'm guessing that this "set" is not designed to use for
> formal variables. That is, you wouldn't want to do something like this:
right.
>
> concat == "x" swap set "y" swap set [y x] "y" unset "x" unset
>
> - "iepos" (Brent Kerby)
>
>
> ------------------------------------------------------------------------
> Get 6 months of FREE* MSN Internet access!
> http://click.egroups.com/1/5727/7/_/_/_/962460780/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
iepos@tunes.org — 2000-07-01 18:45:55
> > I still like making "map" fiddle directly with many items on the stack;
> > it simplifies things.
>
> if the stack was ... A B L P then how would you apply P to items of
> the list L? your proposal makes map too strong.
This is a very interesting question. I think I would avoid leaving things
on the bottom of the stack (like "A" and "B") that I do not immediately
need; instead I would arrange for them to be on the return stack, using
"dip". But, there is an (awkward) solution to your problem[[B, if
we really are in that situation:
[wrap] dip2 dig2 [[call] dip map wrap] dip dip
Here's how it would work... first, we wrap up "A" and "B" using
"[wrap] dip2", leaving the stack as
[A B] L P
Next, we bring the wrapped-up "A" and "B" to the top of the stack with
"dig2" (Joy's "rolldown"), leaving us with
L P [A B]
Then, we dip into the stack, since we do not need "A" and "B" now;
so we have
L P
Now, we do "[call] dip", which will unwrap the list "L", because we
need it to be unwrapped for "map" to work. Finally, we do "map",
which modifies our unwrapped list. Then we "wrap" the list back up.
After we are through with our "dip", we have
L' [A B]
Finally, we want to get "A" and "B" back into their original positions,
so we do another "dip", leaving
A B L'
- "iepos" (Brent Kerby)
stevan apter — 2000-07-03 11:45:10
in joy, + replaces its inputs with its result:
1 2 3 +
1 5
why is this better than just appending:
1 2 3 +
1 2 3 5
sometimes we want this behavior, and sometimes
not. when we don't want this behavior, we have
to pepper our code with dups (and dips and swaps),
for example, when we want to select items from
a list based on some condition on that list:
[10 20 30 40] dup 30 > where swap at
[10 20]
at each point, we know the structure of the stack,
and we know what we want the structure to be.
i can imagine adding two constructs which might
be useful in managing the stack.
first, where f is a function which consumes n
items from the stack, :f is that same function,
except that it appends its result to the stack
without deleting its arguments. offhand, i can't
think how to write : as a combinator; e.g. as
[f] :
remember that f can return more than a single
result.
my other thought, which surfaces now and then
while programming in joy, is that i would like
to have some way of specifying swaps, dups, and
pops in the form of a declarative pattern. for
example, the arrow operator takes two lists and
reorders the stack accordingly:
[X Y Z W] [Z X W] ->
obviously, this could be enhanced to include
pattern-matching, but it seems quite powerful
enough as it is.
Juho Snellman — 2000-07-03 16:53:15
On Mon, Jul 03, 2000 at 07:45:10AM -0400, stevan apter wrote:
> first, where f is a function which consumes n
> items from the stack, :f is that same function,
> except that it appends its result to the stack
> without deleting its arguments. offhand, i can't
> think how to write : as a combinator; e.g. as
>
> [f] :
>
> remember that f can return more than a single
> result.
That looks very much like the 'nullary' combinator in Joy.
Am I missing something?
"
nullary : [P] -> R
Executes P, but no matter how many parameters this consumes,
none are removed from the stack.
"
> my other thought, which surfaces now and then
> while programming in joy, is that i would like
> to have some way of specifying swaps, dups, and
> pops in the form of a declarative pattern. for
> example, the arrow operator takes two lists and
> reorders the stack accordingly:
>
> [X Y Z W] [Z X W] ->
>
> obviously, this could be enhanced to include
> pattern-matching, but it seems quite powerful
> enough as it is.
You mean like this?
(* Find largest element of a list. There must be a more
* idiomatic way for doing this... *)
find-largest == dup first swap [ [<] nullary [swap] [] branch pop ] step;
(* Create a list of 'n' following stack elements. *)
ncons == [] swap [cons] times ;
(* Take two lists. Replace the elements in the second list
* with the corresponding elements of the first list. Destroy
* first list.
*
* ['a 'b 'c] [1 0 2 0] -- ['b 'a 'c 'a]
*)
rearrange-list == [at] map swap pop;
(* Take a list. Create a list of the following 'n' elements in
* the stack ('n' being the largest value in the list).
* Rearrange the second list, and put it's contents on top
* of the stack.
*)
rearrange-stack == [find-largest] nullary 1 + [ncons reverse] cons dip
rearrange-list
i ;
1 2 3 4 5 [1 0 1 0 3] rearrange-stack stack reverse;
-> [1 4 5 4 5 2]
But if you need to juggle the stack so much that this is useful,
the algorithm probably needs rethinking :-)
--
Juho Snellman
"C:stä on kehitetty Massachusettsin teknillisessä korkeakoulussa kieli
nimeltä BCPL."
Louis Madon — 2000-07-03 22:53:51
Juho Snellman wrote:
>
> On Mon, Jul 03, 2000 at 07:45:10AM -0400, stevan apter wrote:
> > first, where f is a function which consumes n
> > items from the stack, :f is that same function,
> > except that it appends its result to the stack
> > without deleting its arguments. offhand, i can't
> > think how to write : as a combinator; e.g. as
> >
> > [f] :
> >
> > remember that f can return more than a single
> > result.
>
> That looks very much like the 'nullary' combinator in Joy.
> Am I missing something?
>
> "
> nullary : [P] -> R
> Executes P, but no matter how many parameters this consumes,
> none are removed from the stack.
> "
Its close, except nullary assumes P returns exactly one value
(interestingly if I try running "1 [pop] nullary" I get a core dump! So
clearly that return value is mandatory)
I believe what Stevan intended was to let P have any number of return
values.
> > my other thought, which surfaces now and then
> > while programming in joy, is that i would like
> > to have some way of specifying swaps, dups, and
> > pops in the form of a declarative pattern. for
> > example, the arrow operator takes two lists and
> > reorders the stack accordingly:
> >
> > [X Y Z W] [Z X W] ->
> >
> > obviously, this could be enhanced to include
> > pattern-matching, but it seems quite powerful
> > enough as it is.
>
> You mean like this?
>
> (* Find largest element of a list. There must be a more
> * idiomatic way for doing this... *)
> find-largest == dup first swap [ [<] nullary [swap] [] branch pop ]
> step;
How about,
find-largest == unswons [max] fold
(both your version and my version would fail on an empty list, so a test
for that should ideally be added).
> (* Create a list of 'n' following stack elements. *)
> ncons == [] swap [cons] times ;
>
> (* Take two lists. Replace the elements in the second list
> * with the corresponding elements of the first list. Destroy
> * first list.
> *
> * ['a 'b 'c] [1 0 2 0] -- ['b 'a 'c 'a]
> *)
> rearrange-list == [at] map swap pop;
>
> (* Take a list. Create a list of the following 'n' elements in
> * the stack ('n' being the largest value in the list).
> * Rearrange the second list, and put it's contents on top
> * of the stack.
> *)
> rearrange-stack == [find-largest] nullary 1 + [ncons reverse] cons dip
> rearrange-list
> i ;
>
> 1 2 3 4 5 [1 0 1 0 3] rearrange-stack stack reverse;
> -> [1 4 5 4 5 2]
Neat idea and with partial evaluation there would be no performance
penalty either. I wonder if Brents' lambda concept could be implemented
in standard Joy using a similar approach?
--
Louis.
stevan apter — 2000-07-04 00:16:44
----- Original Message -----
From: Juho Snellman <jsnell@...>
To: <concatenative@egroups.com>
Sent: Monday, July 03, 2000 12:53 PM
Subject: Re: [stack] three more proposals about Joy
> On Mon, Jul 03, 2000 at 07:45:10AM -0400, stevan apter wrote:
> > first, where f is a function which consumes n
> > items from the stack, :f is that same function,
> > except that it appends its result to the stack
> > without deleting its arguments. offhand, i can't
> > think how to write : as a combinator; e.g. as
> >
> > [f] :
> >
> > remember that f can return more than a single
> > result.
>
> That looks very much like the 'nullary' combinator in Joy.
> Am I missing something?
no - i was. of course, you are right.
thanks.
>
> "
> nullary : [P] -> R
> Executes P, but no matter how many parameters this consumes,
> none are removed from the stack.
> "
>
stevan apter — 2000-07-04 00:57:18
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Monday, July 03, 2000 6:53 PM
Subject: Re: [stack] three more proposals about Joy
> Juho Snellman wrote:
> >
> > On Mon, Jul 03, 2000 at 07:45:10AM -0400, stevan apter wrote:
> > > first, where f is a function which consumes n
> > > items from the stack, :f is that same function,
> > > except that it appends its result to the stack
> > > without deleting its arguments. offhand, i can't
> > > think how to write : as a combinator; e.g. as
> > >
> > > [f] :
> > >
> > > remember that f can return more than a single
> > > result.
> >
> > That looks very much like the 'nullary' combinator in Joy.
> > Am I missing something?
> >
> > "
> > nullary : [P] -> R
> > Executes P, but no matter how many parameters this consumes,
> > none are removed from the stack.
> > "
>
> Its close, except nullary assumes P returns exactly one value
> (interestingly if I try running "1 [pop] nullary" I get a core dump! So
> clearly that return value is mandatory)
>
> I believe what Stevan intended was to let P have any number of return
> values.
oops - forgot about that one. but yes, you're right.
some of the joy combinators still feel unnatural.
>
>
> > > my other thought, which surfaces now and then
> > > while programming in joy, is that i would like
> > > to have some way of specifying swaps, dups, and
> > > pops in the form of a declarative pattern. for
> > > example, the arrow operator takes two lists and
> > > reorders the stack accordingly:
> > >
> > > [X Y Z W] [Z X W] ->
> > >
> > > obviously, this could be enhanced to include
> > > pattern-matching, but it seems quite powerful
> > > enough as it is.
> >
> > You mean like this?
> >
> > (* Find largest element of a list. There must be a more
> > * idiomatic way for doing this... *)
> > find-largest == dup first swap [ [<] nullary [swap] [] branch pop ]
> > step;
>
>
> How about,
>
> find-largest == unswons [max] fold
or
0 [max] over
which does not fail on empties.
>
> (both your version and my version would fail on an empty list, so a test
> for that should ideally be added).
>
>
>
> > (* Create a list of 'n' following stack elements. *)
> > ncons == [] swap [cons] times ;
> >
> > (* Take two lists. Replace the elements in the second list
> > * with the corresponding elements of the first list. Destroy
> > * first list.
> > *
> > * ['a 'b 'c] [1 0 2 0] -- ['b 'a 'c 'a]
> > *)
> > rearrange-list == [at] map swap pop;
> >
> > (* Take a list. Create a list of the following 'n' elements in
> > * the stack ('n' being the largest value in the list).
> > * Rearrange the second list, and put it's contents on top
> > * of the stack.
> > *)
> > rearrange-stack == [find-largest] nullary 1 + [ncons reverse] cons dip
> > rearrange-list
> > i ;
> >
> > 1 2 3 4 5 [1 0 1 0 3] rearrange-stack stack reverse;
> > -> [1 4 5 4 5 2]
>
>
> Neat idea and with partial evaluation there would be no performance
> penalty either. I wonder if Brents' lambda concept could be implemented
> in standard Joy using a similar approach?
in the course of a long drive tonight, i thought about extending
this to nested lists, e.g.
[a [b c d]] [[a b][a c][a d]] ->
the first list would require that the stack contains at least two
items, and that the top of the stack is a list of at least three
items. so e.g.
10 20 30 40 [50 60 70] [a [b c d]] [[a b][a c][a d]] ->
10 20 30 [[40 50][40 60][40 70]]
>
>
> --
> Louis.
>
> ------------------------------------------------------------------------
> Keep in touch with eGroups,
> Keep your long distance bills lower with beMANY!
> http://click.egroups.com/1/4120/7/_/_/_/962664509/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
Louis Madon — 2000-07-04 01:31:38
stevan apter wrote:
> From: Louis Madon <madonl@...>
> > Juho Snellman wrote:
> > >
> > > (* Find largest element of a list. There must be a more
> > > * idiomatic way for doing this... *)
> > > find-largest == dup first swap [ [<] nullary [swap] [] branch pop ]
> > > step;
> >
> >
> > How about,
> >
> > find-largest == unswons [max] fold
>
> or
>
> 0 [max] over
>
> which does not fail on empties.
same as
0 [max] fold
therefore over == fold (ie. obviously we're using differnt names for the
same thing. 'fold' is the name Manfred uses)
However, if you use 0 as the initial value it fails when the elements
are all below zero. Moreover, it is less generic since it won't work on
strings or other objects (whatever the 'max' function can handle which
could be quite a variety of things in principle).
In Haskell, there are two interesting functions called 'minBound' and
'maxBound'. If Joy had them you could write:
minBound [max] fold
which works correctly for empty lists and is completely generic.
'minBound' returns the minimum value possible for the _inferred type_ in
that context. So if we passed a list of integers, minBound would return
(say) -32768, for a list of strings it would be "" etc.
Defining such functions is not possible in Joy without adding a static
overloading mechanism which is one of the many reasons why I believe Joy
needs a good static type system.
--
Louis.
Louis Madon — 2000-07-04 02:18:58
stevan apter wrote:
>
> in the course of a long drive tonight, i thought about extending
> this to nested lists, e.g.
I hope you didn't cause too many accidents along the way ;-)
>
> [a [b c d]] [[a b][a c][a d]] ->
>
> the first list would require that the stack contains at least two
> items, and that the top of the stack is a list of at least three
> items. so e.g.
>
> 10 20 30 40 [50 60 70] [a [b c d]] [[a b][a c][a d]] ->
> 10 20 30 [[40 50][40 60][40 70]]
So you have a 'from' pattern and a 'to' pattern and -> rearranges data
accordingly. This could effectively be the lambda idea though you'd need
one additional refinement: change the 'to' pattern into a _template_
which is instantiated using the matched input values, eg:
[a [b c]] [a c + b] ->
would be the same as
unpair swap [+] dip
though it seems (at least in this case) we're just trading stack noise
for symbol noise.
This is nonetheless quite interesting because we can view the 'from'
pattern as formal variables and the 'to' template as a function body. So
in effect lambdas can be supported in _standard_ Joy. I like that.
--
Louis.
Juho Snellman — 2000-07-04 04:24:00
On Tue, Jul 04, 2000 at 08:53:51AM +1000, Louis Madon wrote:
> find-largest == unswons [max] fold
>
> (both your version and my version would fail on an empty list, so a test
> for that should ideally be added).
Hmm... I don't see fold in Manfred's Joy. Luckily, I think that
uncons [max] step should work too ;-)
> Neat idea and with partial evaluation there would be no performance
> penalty either. I wonder if Brents' lambda concept could be implemented
> in standard Joy using a similar approach?
Yup. Just a bit more difficult. This seems to work, though
I suspect there are borderline cases that fail. If you can
stomach badly structured code, here it is:
---
(* This code is only proof of concept. Please rewrite before
* using it for anything real.
*
* Tested to work on standard Joy.
*
* A lambda is first made of a list of symbols and the stack,
* taking the form of a dictionary-list. A quotation using
* those symbols is given along with the lambda-dictionary to
* do-lambda, which then recursively replaces the symbols with
* the values.
*)
ncons == [] swap [cons] times;
(* Make a dictionary out of a key list and a value list *)
d_make == (* Add empty list below the two argument lists *)
[] swap [swap] dip
(* Loop over keys *)
[
(* Get the corresponding value *)
[unswons] dip
(* Create list containing key/value*)
[] cons cons [] cons
(* Add key/value to the dictionart list *)
swap [concat] dip
]
step pop;
(* Lookup value for key in a dictionary. If key not found,
* return key. Not compatible with the typlib.joy dictionary
* stuff, I think. *)
d_look == [dup [] cons swap] dip
[
(* Make backup copies *)
[dup] dip dup first [swap] dip
(* If key found, store value in list
* above the dictionary. *)
equal [ rest i swap [swons] dip ] [ pop ] branch
]
step
pop first
;
(* Loop through all elements of a list. Add value produced
* by dictionary lookup into the result list. If a list
* is encountered, recurse. *)
(* Rewrite using a recursion-combinator. Which one? *)
apply-lambda ==
(* Create new list *)
[] swap [swap] dip
[
[ list ]
[ apply-lambda ]
(* Ouch... This needs some serious looking at... *)
[ [dup] dip swap d_look swap [swons] dip ]
ifte
]
step
[reverse swons] dip
;
(* Prepare an inital list for apply-lambda, and remove
* the dictionary from the stack when ready. *)
do-lambda == [] swap [swap] dip apply-lambda pop i;
(* Take a list of symbols, and take a value from
* the stack for each of them. *)
make-lambda == (* Amount of variables *)
dup size
(* Create list of 'n' values from stack *)
[ncons] cons dip swap
(* Create a variable -> value mapping *)
d_make;
---
Usage:
joy> 0 4 5 6 [ r j s ] make-lambda
joy> [j [r s *] i -] do-lambda sr i sr;
STACK:
>> [5 [4 6 *] i -]
>> 0
STACK:
>> -19
>> 0
joy>
Sick, eh :-)
Lambdas can of course be nested, but if two nested lambdas
use the same formal variables, the latter's symbols would
be substituted twice. Not that anyone would actually bother
writing such code in Joy... This also shows, that raw
functions on the stack are very useful indeed.
Stevan, does this run on conk?
--
Juho Snellman
"C:stä on kehitetty Massachusettsin teknillisessä korkeakoulussa kieli
nimeltä BCPL."
Louis Madon — 2000-07-04 06:55:01
Juho Snellman wrote:
> On Tue, Jul 04, 2000 at 08:53:51AM +1000, Louis Madon wrote:
> > find-largest == unswons [max] fold
> >
> > (both your version and my version would fail on an empty list, so a
> test
> > for that should ideally be added).
>
> Hmm... I don't see fold in Manfred's Joy.
I don't think he's implemented it but it is documented in his paper
"Atomic Programs of Joy" in the section "Combinators for Aggregate
Types"
> Luckily, I think that
> uncons [max] step should work too ;-)
Yes, interesting, that does indeed work. In fact, I can now see that
'step' is the same as 'fold' but with the stack as the accumulating
value. There is obviously no equivalent to this in applicative
languages.
With this insight, fold can be implemented simply as
fold == swap step
Maybe thats why Manfred didn't implement it :-)
> > Neat idea and with partial evaluation there would be no performance
> > penalty either. I wonder if Brents' lambda concept could be
> implemented
> > in standard Joy using a similar approach?
>
> Yup. Just a bit more difficult. This seems to work, though
> I suspect there are borderline cases that fail. If you can
> stomach badly structured code, here it is:
>
> ---
> (* This code is only proof of concept. Please rewrite before
> * using it for anything real.
> *
> * Tested to work on standard Joy.
> *
> * A lambda is first made of a list of symbols and the stack,
> * taking the form of a dictionary-list. A quotation using
> * those symbols is given along with the lambda-dictionary to
> * do-lambda, which then recursively replaces the symbols with
> * the values.
> *)
>
> ncons == [] swap [cons] times;
>
> (* Make a dictionary out of a key list and a value list *)
>
> d_make == (* Add empty list below the two argument lists *)
> [] swap [swap] dip
> (* Loop over keys *)
> [
> (* Get the corresponding value *)
> [unswons] dip
> (* Create list containing key/value*)
> [] cons cons [] cons
> (* Add key/value to the dictionart list *)
> swap [concat] dip
> ]
> step pop;
>
> (* Lookup value for key in a dictionary. If key not found,
> * return key. Not compatible with the typlib.joy dictionary
> * stuff, I think. *)
> d_look == [dup [] cons swap] dip
> [
> (* Make backup copies *)
> [dup] dip dup first [swap] dip
> (* If key found, store value in list
> * above the dictionary. *)
> equal [ rest i swap [swons] dip ] [ pop ] branch
> ]
> step
> pop first
> ;
>
> (* Loop through all elements of a list. Add value produced
> * by dictionary lookup into the result list. If a list
> * is encountered, recurse. *)
> (* Rewrite using a recursion-combinator. Which one? *)
> apply-lambda ==
> (* Create new list *)
> [] swap [swap] dip
> [
> [ list ]
> [ apply-lambda ]
> (* Ouch... This needs some serious looking at... *)
> [ [dup] dip swap d_look swap [swons] dip ]
> ifte
> ]
> step
> [reverse swons] dip
> ;
>
> (* Prepare an inital list for apply-lambda, and remove
> * the dictionary from the stack when ready. *)
> do-lambda == [] swap [swap] dip apply-lambda pop i;
>
> (* Take a list of symbols, and take a value from
> * the stack for each of them. *)
> make-lambda == (* Amount of variables *)
> dup size
> (* Create list of 'n' values from stack *)
> [ncons] cons dip swap
> (* Create a variable -> value mapping *)
> d_make;
>
> ---
>
> Usage:
>
> joy> 0 4 5 6 [ r j s ] make-lambda
> joy> [j [r s *] i -] do-lambda sr i sr;
> STACK:
> >> [5 [4 6 *] i -]
> >> 0
> STACK:
> >> -19
> >> 0
> joy>
>
> Sick, eh :-)
Wow! and you did that on your coffee break right? :-)
So the '->' combinator discussed in my other message can now be
implemented as:
-> == [make-lambda] dip do-lambda
Now that its clear we can have lambdas and even have an implementation
of them it will be interesting to see just how useful they really are.
BTW, a couple of (really) minor nits with respect to your code:
'd_make' is already defined as 'zip', so d_make == zip
some of the sequences you used also have definitions:
swap [swap] dip == rollup
[] cons == unitlist
[] cons cons == pair
[dup] dip == dupd
[swap] dip == swapd
>
> Lambdas can of course be nested, but if two nested lambdas
> use the same formal variables, the latter's symbols would
> be substituted twice. Not that anyone would actually bother
> writing such code in Joy... This also shows, that raw
> functions on the stack are very useful indeed.
I fully agree.
--
Louis.
Louis Madon — 2000-07-04 08:15:52
Louis Madon wrote:
> So the '->' combinator discussed in my other message can now be
> implemented as:
>
> -> == [make-lambda] dip do-lambda
Oops, there should be an 'i' on the end of that, ie.
-> == [make-lambda] dip do-lambda i
I just tried using it to rewrite the 'mkDepUpd' helper function in my
topsort routine:
(* original *)
mkDepUpd == unitlist [unpair] swoncat [setdiff pair] concat;
(* rewrite *)
mkDepUpd == [x] [[unpair x setdiff pair]] ->;
It works beautifully and is clearly simpler than the original. (BTW I
actually used the name 'lambda' rather than '->' since standard Joy
complains about the syntax).
--
Louis.
stevan apter — 2000-07-04 13:47:56
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Monday, July 03, 2000 9:31 PM
Subject: Re: [stack] three more proposals about Joy
> stevan apter wrote:
> > From: Louis Madon <madonl@...>
> > > Juho Snellman wrote:
> > > >
> > > > (* Find largest element of a list. There must be a more
> > > > * idiomatic way for doing this... *)
> > > > find-largest == dup first swap [ [<] nullary [swap] [] branch pop ]
> > > > step;
> > >
> > >
> > > How about,
> > >
> > > find-largest == unswons [max] fold
> >
> > or
> >
> > 0 [max] over
> >
> > which does not fail on empties.
>
> same as
>
> 0 [max] fold
>
> therefore over == fold (ie. obviously we're using differnt names for the
> same thing. 'fold' is the name Manfred uses)
fold is over restricted to binary functions. over takes a function of
any arity > 1:
[[40 50 60][70 80 90]]0[* +] scan
[0 70 3580 214890]
[[40 50 60][70 80 90]]0[* +] scan
[0 70 3580 214890]
>
> However, if you use 0 as the initial value it fails when the elements
> are all below zero.
?
[-1 -2 -3 -4] 0 [max] over
0
unless you want the result to always be drawn from the list:
[-1 -2 -3 -4] 0N [max] over
-1
where 0N is minint.
> Moreover, it is less generic since it won't work on
> strings or other objects (whatever the 'max' function can handle which
> could be quite a variety of things in principle).
when this is a concern, the initial value should be computed from
the type of the argument(s).
"abcdef" 0 [max] over
'f
>
> In Haskell, there are two interesting functions called 'minBound' and
> 'maxBound'. If Joy had them you could write:
>
> minBound [max] fold
>
> which works correctly for empty lists and is completely generic.
> 'minBound' returns the minimum value possible for the _inferred type_ in
> that context. So if we passed a list of integers, minBound would return
> (say) -32768, for a list of strings it would be "" etc.
right.
>
> Defining such functions is not possible in Joy without adding a static
> overloading mechanism which is one of the many reasons why I believe Joy
> needs a good static type system.
>
>
> --
> Louis.
>
> ------------------------------------------------------------------------
> Free, Unlimited Calls Anywhere!
> Conference in the whole family on the same call.
> Let the fights begin! Visit Firetalk.com - Click below.
> http://click.egroups.com/1/5476/7/_/_/_/962673982/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
stevan apter — 2000-07-04 13:56:43
----- Original Message -----
From: Juho Snellman <jsnell@...>
To: <concatenative@egroups.com>
Sent: Tuesday, July 04, 2000 12:24 AM
Subject: Re: [stack] three more proposals about Joy
> On Tue, Jul 04, 2000 at 08:53:51AM +1000, Louis Madon wrote:
> > find-largest == unswons [max] fold
> >
> > (both your version and my version would fail on an empty list, so a test
> > for that should ideally be added).
>
> Hmm... I don't see fold in Manfred's Joy. Luckily, I think that
> uncons [max] step should work too ;-)
>
> > Neat idea and with partial evaluation there would be no performance
> > penalty either. I wonder if Brents' lambda concept could be implemented
> > in standard Joy using a similar approach?
>
> Yup. Just a bit more difficult. This seems to work, though
> I suspect there are borderline cases that fail. If you can
> stomach badly structured code, here it is:
>
> ---
> (* This code is only proof of concept. Please rewrite before
> * using it for anything real.
> *
> * Tested to work on standard Joy.
> *
> * A lambda is first made of a list of symbols and the stack,
> * taking the form of a dictionary-list. A quotation using
> * those symbols is given along with the lambda-dictionary to
> * do-lambda, which then recursively replaces the symbols with
> * the values.
> *)
>
> ncons == [] swap [cons] times;
>
> (* Make a dictionary out of a key list and a value list *)
>
> d_make == (* Add empty list below the two argument lists *)
> [] swap [swap] dip
> (* Loop over keys *)
> [
> (* Get the corresponding value *)
> [unswons] dip
> (* Create list containing key/value*)
> [] cons cons [] cons
> (* Add key/value to the dictionart list *)
> swap [concat] dip
> ]
> step pop;
>
> (* Lookup value for key in a dictionary. If key not found,
> * return key. Not compatible with the typlib.joy dictionary
> * stuff, I think. *)
> d_look == [dup [] cons swap] dip
>
> (* Make backup copies *)
> [dup] dip dup first [swap] dip
> (* If key found, store value in list
> * above the dictionary. *)
> equal [ rest i swap [swons] dip ] [ pop ] branch
> ]
> step
> pop first
> ;
>
> (* Loop through all elements of a list. Add value produced
> * by dictionary lookup into the result list. If a list
> * is encountered, recurse. *)
> (* Rewrite using a recursion-combinator. Which one? *)
> apply-lambda ==
> (* Create new list *)
> [] swap [swap] dip
> [
> [ list ]
> [ apply-lambda ]
> (* Ouch... This needs some serious looking at... *)
> [ [dup] dip swap d_look swap [swons] dip ]
> ifte
> ]
> step
> [reverse swons] dip
> ;
>
> (* Prepare an inital list for apply-lambda, and remove
> * the dictionary from the stack when ready. *)
> do-lambda == [] swap [swap] dip apply-lambda pop i;
>
> (* Take a list of symbols, and take a value from
> * the stack for each of them. *)
> make-lambda == (* Amount of variables *)
> dup size
> (* Create list of 'n' values from stack *)
> [ncons] cons dip swap
> (* Create a variable -> value mapping *)
> d_make;
>
> ---
>
> Usage:
>
> joy> 0 4 5 6 [ r j s ] make-lambda
> joy> [j [r s *] i -] do-lambda sr i sr;
> STACK:
> >> [5 [4 6 *] i -]
> >> 0
> STACK:
> >> -19
> >> 0
> joy>
>
> Sick, eh :-)
>
> Lambdas can of course be nested, but if two nested lambdas
> use the same formal variables, the latter's symbols would
> be substituted twice. Not that anyone would actually bother
> writing such code in Joy... This also shows, that raw
> functions on the stack are very useful indeed.
>
> Stevan, does this run on conk?
with some fiddling, it should. currently, joy-according-to-the-
manual and conk diverge in certain areas, so i wouldn't expect
anything but the simplest joy code to run without modification.
this hasn't been a problem up til now, because we've avoided
code with as much detail as yours above. the lambda topic is
interesting, though.
>
> --
> Juho Snellman
> "C:stä on kehitetty Massachusettsin teknillisessä korkeakoulussa kieli
> nimeltä BCPL."
>
> ------------------------------------------------------------------------
> 0% Introductory APR!
> Instant Approval!
> Aria Visa - get yours today.
> http://click.egroups.com/1/6035/7/_/_/_/962684642/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
Juho Snellman — 2000-07-04 13:59:36
On Tue, Jul 04, 2000 at 04:55:01PM +1000, Louis Madon wrote:
> > Sick, eh :-)
>
> Wow! and you did that on your coffee break right? :-)
Uhh... "But this is vital for my work, Boss!" ;-)
> Now that its clear we can have lambdas and even have an implementation
> of them it will be interesting to see just how useful they really are.
I'm guessing, that they'll be mainly good for mathematical stuff.
Heavy equations are hard to do without lots of juggling. However,
for normal use the lambdas are a bad idea, IMHO. By using them,
the easy refactoring advantage is lost, since we again need to
worry about scope.
> BTW, a couple of (really) minor nits with respect to your code:
>
> 'd_make' is already defined as 'zip', so d_make == zip
You. Actually, I noticed that d_make wasn't even needed
with some rewriting. I also fixed apply-lambda to use map,
which makes it and do-lambda actually pretty.
> some of the sequences you used also have definitions:
>
> swap [swap] dip == rollup
> [] cons == unitlist
> [] cons cons == pair
Ah, I had totally forgot these. Thanks :-)
> [dup] dip == dupd
> [swap] dip == swapd
While I think that these are better expressed using the original,
or more expressive names. So I proabably won't use them much.
But here is a better version:
---
(* Tested to work on standard Joy.
*
* A lambda is first made of a list of symbols and the stack,
* taking the form of a dictionary-list. A quotation using
* those symbols is given along with the lambda-dictionary to
* do-lambda, which then recursively replaces the symbols with
* the values.
*)
(* Initialize result-list with key *)
d_look_init_list == [dup unitlist swap] dip;
(* Lookup value for key in a dictionary. If key not found,
* return key. Not compatible with the typlib.joy dictionary
* stuff, I think. *)
d_look == d_look_init_list
[
(* Unpack dictionary pair *)
unpair rollup
(* If key found, store value in result-list *)
[equal] [ pop [swons] dip ] [pop swap pop] ifte
]
step
pop first;
(* Replace all elements with the results of a dictionary
* lookup for that element. If a list is encountered,
* recurse. *)
apply-lambda == [
[ list ]
[ apply-lambda ]
[ swap [d_look] nullary ]
ifte
]
map;
(* Do an apply-lambda, and remove the dictinary when ready. *)
do-lambda == apply-lambda swap pop;
(* Loop through a reverse list of symbols, take the top
* value from the stack for each of them, and create
* a dictionary out of it. *)
make-lambda == reverse
[] swap
[ swap [swap pair] dip cons ] step;
---
--
Juho Snellman
"C:stä on kehitetty Massachusettsin teknillisessä korkeakoulussa kieli
nimeltä BCPL."
stevan apter — 2000-07-04 14:14:00
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Tuesday, July 04, 2000 2:55 AM
Subject: Re: [stack] three more proposals about Joy
>
> With this insight, fold can be implemented simply as
>
> fold == swap step
fold == [swap] dip step
or are assuming different stack setups?
[10 20 30 40 50] 0 [max] fold
50
stevan apter — 2000-07-04 15:23:04
no surprise, but this won't run in conk, even after i make
the obvious substitutions. i want to align conk with joy,
now that we have some interesting new code to play with.
juho, can you provide one or two simple examples for this?
----- Original Message -----
From: Juho Snellman <jsnell@...>
To: <concatenative@egroups.com>
Sent: Tuesday, July 04, 2000 9:59 AM
Subject: Re: [stack] three more proposals about Joy
> On Tue, Jul 04, 2000 at 04:55:01PM +1000, Louis Madon wrote:
> > > Sick, eh :-)
> >
> > Wow! and you did that on your coffee break right? :-)
>
> Uhh... "But this is vital for my work, Boss!" ;-)
>
> > Now that its clear we can have lambdas and even have an implementation
> > of them it will be interesting to see just how useful they really are.
>
> I'm guessing, that they'll be mainly good for mathematical stuff.
> Heavy equations are hard to do without lots of juggling. However,
> for normal use the lambdas are a bad idea, IMHO. By using them,
> the easy refactoring advantage is lost, since we again need to
> worry about scope.
>
> > BTW, a couple of (really) minor nits with respect to your code:
> >
> > 'd_make' is already defined as 'zip', so d_make == zip
>
> You. Actually, I noticed that d_make wasn't even needed
> with some rewriting. I also fixed apply-lambda to use map,
> which makes it and do-lambda actually pretty.
>
> > some of the sequences you used also have definitions:
> >
> > swap [swap] dip == rollup
> > [] cons == unitlist
> > [] cons cons == pair
>
> Ah, I had totally forgot these. Thanks :-)
>
> > [dup] dip == dupd
> > [swap] dip == swapd
>
> While I think that these are better expressed using the original,
> or more expressive names. So I proabably won't use them much.
>
> But here is a better version:
>
> ---
>
> (* Tested to work on standard Joy.
> *
> * A lambda is first made of a list of symbols and the stack,
> * taking the form of a dictionary-list. A quotation using
> * those symbols is given along with the lambda-dictionary to
> * do-lambda, which then recursively replaces the symbols with
> * the values.
> *)
>
> (* Initialize result-list with key *)
> d_look_init_list == [dup unitlist swap] dip;
>
> (* Lookup value for key in a dictionary. If key not found,
> * return key. Not compatible with the typlib.joy dictionary
> * stuff, I think. *)
> d_look == d_look_init_list
>
> (* Unpack dictionary pair *)
> unpair rollup
> (* If key found, store value in result-list *)
> [equal] [ pop [swons] dip ] [pop swap pop] ifte
> ]
> step
> pop first;
>
> (* Replace all elements with the results of a dictionary
> * lookup for that element. If a list is encountered,
> * recurse. *)
> apply-lambda == [
> [ list ]
> [ apply-lambda ]
> [ swap [d_look] nullary ]
> ifte
> ]
> map;
>
> (* Do an apply-lambda, and remove the dictinary when ready. *)
> do-lambda == apply-lambda swap pop;
>
> (* Loop through a reverse list of symbols, take the top
> * value from the stack for each of them, and create
> * a dictionary out of it. *)
> make-lambda == reverse
> [] swap
> [ swap [swap pair] dip cons ] step;
>
> ---
>
> --
> Juho Snellman
> "C:stä on kehitetty Massachusettsin teknillisessä korkeakoulussa kieli
> nimeltä BCPL."
>
> ------------------------------------------------------------------------
> Free, Unlimited Calls Anywhere!
> Visit Firetalk.com - click below.
> http://click.egroups.com/1/5479/7/_/_/_/962719179/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
stevan apter — 2000-07-04 18:26:35
got it. very nice - and you handle nested 'from' templates.
i don't handle dashes in names, so a little renaming was necessary.
--
d_look_init_list == [dup unitlist swap] dip;
d_look ==
d_look_init_list
[
unpair rollup
[equal] [ pop [swons] dip ] [pop swap pop] ifte
] step
pop first;
apply_lambda ==
[
[list]
[apply_lambda]
[swap [d_look] nullary]
ifte
] map;
do_lambda == apply_lambda swap pop;
make_lambda ==
reverse [] swap
[
swap [swap pair] dip cons
] step;
ld == [make_lambda] dip do_lambda;
conk ..
"lambda" load
[d_look_init_list d_look apply_lambda do_lambda make_lambda ld]
()
10 20 30 [x y z][x y +] ld
[10 20 add]
i
30
()
10 [20 30][40 50][[x][y]] [x y +] ->
[20 30] [40 50] [20 40 add]
stevan apter — 2000-07-04 19:23:07
----- Original Message -----
From: stevan apter <apter@...>
To: <concatenative@egroups.com>
Sent: Tuesday, July 04, 2000 2:26 PM
Subject: Re: [stack] three more proposals about Joy
> got it. very nice - and you handle nested 'from' templates.
:
>
> 10 [20 30][40 50][[x][y]] [x y +] ->
> [20 30] [40 50] [20 40 add]
my mistake - it doesn't appear to (that's my k implementation above).
is your stuff supposed to understand templates like [[x][y]]?
>
>
> ------------------------------------------------------------------------
> Free Conference Calling with Firetalk!
> Click Here!
> http://click.egroups.com/1/5480/7/_/_/_/962734646/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
Louis Madon — 2000-07-04 21:03:09
stevan apter wrote:
> fold is over restricted to binary functions. over takes a function of
> any arity > 1:
>
> [[40 50 60][70 80 90]]0[* +] scan
> [0 70 3580 214890]
> [[40 50 60][70 80 90]]0[* +] scan
> [0 70 3580 214890]
Oh Ok, thats useful.
>
> >
> > However, if you use 0 as the initial value it fails when the
> elements
> > are all below zero.
>
> ?
>
> [-1 -2 -3 -4] 0 [max] over
> 0
Sorry I was thinking of 'min' - my whole message was probably confusing
as a result.
>
> unless you want the result to always be drawn from the list:
>
> [-1 -2 -3 -4] 0N [max] over
> -1
No I had meant the minimum, nonetheless passing minint will work for
integers.
> where 0N is minint.
>
> > Moreover, it is less generic since it won't work on
> > strings or other objects (whatever the 'max' function can handle
> which
> > could be quite a variety of things in principle).
>
> when this is a concern, the initial value should be computed from
> the type of the argument(s).
>
> "abcdef" 0 [max] over
> 'f
I don't follow, you're still using 0 as the initial value, what if I do:
["the" "quick" "brown" "fox"] 0 [min] over
will that return "brown" ?
Louis Madon — 2000-07-04 21:05:32
stevan apter wrote:
>
> ----- Original Message -----
> From: Louis Madon <madonl@...>
> To: <concatenative@egroups.com>
> Sent: Tuesday, July 04, 2000 2:55 AM
> Subject: Re: [stack] three more proposals about Joy
>
> >
> > With this insight, fold can be implemented simply as
> >
> > fold == swap step
>
> fold == [swap] dip step
>
> or are assuming different stack setups?
>
> [10 20 30 40 50] 0 [max] fold
> 50
>
Oh ofcourse, you are right. Thats what can happen when you spend a whole
of three seconds writing code :-)
--
Louis.
stevan apter — 2000-07-04 22:02:09
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Tuesday, July 04, 2000 5:03 PM
Subject: Re: [stack] three more proposals about Joy
> >
> > when this is a concern, the initial value should be computed from
> > the type of the argument(s).
> >
> > "abcdef" 0 [max] over
> > 'f
>
> I don't follow, you're still using 0 as the initial value,
"abcdef" 0 [max] scan
[0 'a 'b 'c 'd 'e 'f]
a is greater than 0, b is greater than a, ... f is the greatest.
> what if I do:
>
> ["the" "quick" "brown" "fox"] 0 [min] over
>
> will that return "brown" ?
no - the strings differ in length, so you can't compare them.
>
> ------------------------------------------------------------------------
> Free Conference Calling with Firetalk!
> Host your next egroup meeting live on Firetalk.
> Click here!
> http://click.egroups.com/1/5478/7/_/_/_/962744265/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
Louis Madon — 2000-07-04 22:12:09
stevan apter wrote:
> > what if I do:
> >
> > ["the" "quick" "brown" "fox"] 0 [min] over
> >
> > will that return "brown" ?
>
> no - the strings differ in length, so you can't compare them.
Ok, that was my point - you've lost some genericity. So you'll need
separate functions for 'find_largest_string', 'find_largest_vector',
etc.
--
Louis.
stevan apter — 2000-07-04 22:26:26
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Tuesday, July 04, 2000 6:12 PM
Subject: Re: [stack] three more proposals about Joy
> stevan apter wrote:
> > > what if I do:
> > >
> > > ["the" "quick" "brown" "fox"] 0 [min] over
> > >
> > > will that return "brown" ?
> >
> > no - the strings differ in length, so you can't compare them.
>
> Ok, that was my point - you've lost some genericity. So you'll need
> separate functions for 'find_largest_string', 'find_largest_vector',
> etc.
but max is an atomic function, so it doesn't make sense to
ask for the max of strings of different length.
>
>
> --
> Louis.
>
> ------------------------------------------------------------------------
> Get 6 months of FREE* MSN Internet access!
> http://click.egroups.com/1/5727/7/_/_/_/962748405/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
Louis Madon — 2000-07-04 22:46:37
stevan apter wrote:
>
> ----- Original Message -----
> From: Louis Madon <madonl@...>
> To: <concatenative@egroups.com>
> Sent: Tuesday, July 04, 2000 6:12 PM
> Subject: Re: [stack] three more proposals about Joy
>
> > stevan apter wrote:
> > > > what if I do:
> > > >
> > > > ["the" "quick" "brown" "fox"] 0 [min] over
> > > >
> > > > will that return "brown" ?
> > >
> > > no - the strings differ in length, so you can't compare them.
> >
> > Ok, that was my point - you've lost some genericity. So you'll need
> > separate functions for 'find_largest_string', 'find_largest_vector',
> > etc.
>
> but max is an atomic function, so it doesn't make sense to
> ask for the max of strings of different length.
>
I really wonder why though. '<' is polymorphic and max can be trivially
defined in terms of it:
max == [<] [popd] [pop] ifte
In general, it makes sense for max/min to be defined on any type that
has a total order.
--
Louis.
Juho Snellman — 2000-07-04 22:42:26
On Tue, Jul 04, 2000 at 03:23:07PM -0400, stevan apter wrote:
> From: stevan apter <apter@...>
> > got it. very nice - and you handle nested 'from' templates.
> :
> >
> > 10 [20 30][40 50][[x][y]] [x y +] ->
> > [20 30] [40 50] [20 40 add]
>
> my mistake - it doesn't appear to (that's my k implementation above).
> is your stuff supposed to understand templates like [[x][y]]?
Nope, didn't even think of that. I believe this does what
you mean (except that it eats the arguments, unlike your
version):
---
(* Helpers *)
get_value == [ [unswons] dip ] dip;
add_pair == rolldown pair swons;
remove_list == popd;
(* Add the first and third elements from the top of
* the stack as a key/value pair into the list that
* is the second element. If first element is list,
* assume third element is also a list with at least
* as many elements and recurse. *)
d_add ==
[ list ]
[ [ get_value d_add ] step remove_list]
[ add_pair ]
ifte;
(* Loop through a reverse list of symbols, take the top
* value from the stack for each of them, and create
* a dictionary out of it. *)
make-lambda == reverse
[] swap
[ d_add ] step ;
---
joy> newstack 0 [1 [2 3] 4] 5 [[a [b x] c] d] make-lambda sr;
STACK:
>> [[c 4] [x 3] [b 2] [a 1] [d 5]]
>> 0
--
Juho Snellman
"C:stä on kehitetty Massachusettsin teknillisessä korkeakoulussa kieli
nimeltä BCPL."
stevan apter — 2000-07-04 23:36:03
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Tuesday, July 04, 2000 6:46 PM
Subject: Re: [stack] three more proposals about Joy
> stevan apter wrote:
> >
> > ----- Original Message -----
> > From: Louis Madon <madonl@...>
> > To: <concatenative@egroups.com>
> > Sent: Tuesday, July 04, 2000 6:12 PM
> > Subject: Re: [stack] three more proposals about Joy
> >
> > > stevan apter wrote:
> > > > > what if I do:
> > > > >
> > > > > ["the" "quick" "brown" "fox"] 0 [min] over
> > > > >
> > > > > will that return "brown" ?
> > > >
> > > > no - the strings differ in length, so you can't compare them.
> > >
> > > Ok, that was my point - you've lost some genericity. So you'll need
> > > separate functions for 'find_largest_string', 'find_largest_vector',
> > > etc.
> >
> > but max is an atomic function, so it doesn't make sense to
> > ask for the max of strings of different length.
> >
>
> I really wonder why though. '<' is polymorphic and max can be trivially
> defined in terms of it:
>
> max == [<] [popd] [pop] ifte
>
>
> In general, it makes sense for max/min to be defined on any type that
> has a total order.
i think what we have here is a failure to communictae. ;-)
as in k above, so in conk below: a string is a list of characters:
['a 'b 'c 'd]
"abcd"
first
'a
more precisely, "abcdef" is "vector notation" for the list of length
six whose first element is the atom 'a, &c.
since max and > are atomic operations, they require that their operands
are conformable. that's why
"abcd" 'b >
[1 0 0 0]
(remember - x y > means: y > x, not x > y).
and
"abcd" 'b max
"bbcd"
>
>
> --
> Louis.
>
> ------------------------------------------------------------------------
> Need a credit card?
> Instant Approval and 0% intro APR with Aria!
> http://click.egroups.com/1/6034/7/_/_/_/962750481/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
Louis Madon — 2000-07-05 00:01:39
stevan apter wrote:
>
> ----- Original Message -----
> From: Louis Madon <madonl@...>
> To: <concatenative@egroups.com>
> Sent: Tuesday, July 04, 2000 6:46 PM
> Subject: Re: [stack] three more proposals about Joy
>
> > stevan apter wrote:
> > >
> > > ----- Original Message -----
> > > From: Louis Madon <madonl@...>
> > > To: <concatenative@egroups.com>
> > > Sent: Tuesday, July 04, 2000 6:12 PM
> > > Subject: Re: [stack] three more proposals about Joy
> > >
> > > > stevan apter wrote:
> > > > > > what if I do:
> > > > > >
> > > > > > ["the" "quick" "brown" "fox"] 0 [min] over
> > > > > >
> > > > > > will that return "brown" ?
> > > > >
> > > > > no - the strings differ in length, so you can't compare them.
> > > >
> > > > Ok, that was my point - you've lost some genericity. So you'll
> need
> > > > separate functions for 'find_largest_string',
> 'find_largest_vector',
> > > > etc.
> > >
> > > but max is an atomic function, so it doesn't make sense to
> > > ask for the max of strings of different length.
> > >
> >
> > I really wonder why though. '<' is polymorphic and max can be
> trivially
> > defined in terms of it:
> >
> > max == [<] [popd] [pop] ifte
> >
> >
> > In general, it makes sense for max/min to be defined on any type
> that
> > has a total order.
>
> i think what we have here is a failure to communictae. ;-)
unfortunately, the link still seems to be down :-(
> as in k above, so in conk below: a string is a list of characters:
>
> ['a 'b 'c 'd]
> "abcd"
> first
> 'a
>
> more precisely, "abcdef" is "vector notation" for the list of length
> six whose first element is the atom 'a, &c.
Sure, Joy and Haskell do the same.
>
> since max and > are atomic operations, they require that their
> operands
> are conformable. that's why
>
> "abcd" 'b >
> [1 0 0 0]
That seems an overly restrictive definition of '>'. How are you going to
sort strings? If the answer is by using a special string comparison
function (ie. not '>') then my point stands: your code is less generic
than it could be.
>
> (remember - x y > means: y > x, not x > y).
Yes, I remember you mentioning that conk works this way some time back,
but I don't see that this has any relevance to genericity.
> and
>
> "abcd" 'b max
> "bbcd"
This is a different operation than returning the maximum of two values:
its max applied to a list, ie. [max] cons map. I would therefore give
it a different name. The reason is, if you want code to be both generic
and readable, every name should have _one meaning_. Overloading the
same name with different meanings is what gives overloading a bad name
(no pun intended :-).
--
Louis.
Louis Madon — 2000-07-05 00:38:41
Louis Madon wrote:
> stevan apter wrote:
> > as in k above, so in conk below: a string is a list of characters:
> >
> > ['a 'b 'c 'd]
> > "abcd"
> > first
> > 'a
> >
> > more precisely, "abcdef" is "vector notation" for the list of length
> > six whose first element is the atom 'a, &c.
>
> Sure, Joy and Haskell do the same.
Actually, Joy doesn't quite have this, though I believe it should.
Specifically, in Joy "abcd" first is 'a but ['a 'b 'c 'd] is not "abcd".
--
Louis.
stevan apter — 2000-07-05 04:39:43
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Tuesday, July 04, 2000 8:01 PM
Subject: Re: [stack] three more proposals about Joy
> stevan apter wrote:
> >
> > ----- Original Message -----
> > From: Louis Madon <madonl@...>
> > To: <concatenative@egroups.com>
> > Sent: Tuesday, July 04, 2000 6:46 PM
> > Subject: Re: [stack] three more proposals about Joy
> >
> > > stevan apter wrote:
> > > >
> > > > ----- Original Message -----
> > > > From: Louis Madon <madonl@...>
> > > > To: <concatenative@egroups.com>
> > > > Sent: Tuesday, July 04, 2000 6:12 PM
> > > > Subject: Re: [stack] three more proposals about Joy
> > > >
> > > > > stevan apter wrote:
> > > > > > > what if I do:
> > > > > > >
> > > > > > > ["the" "quick" "brown" "fox"] 0 [min] over
> > > > > > >
> > > > > > > will that return "brown" ?
> > > > > >
> > > > > > no - the strings differ in length, so you can't compare them.
> > > > >
> > > > > Ok, that was my point - you've lost some genericity. So you'll
> > need
> > > > > separate functions for 'find_largest_string',
> > 'find_largest_vector',
> > > > > etc.
> > > >
> > > > but max is an atomic function, so it doesn't make sense to
> > > > ask for the max of strings of different length.
> > > >
> > >
> > > I really wonder why though. '<' is polymorphic and max can be
> > trivially
> > > defined in terms of it:
> > >
> > > max == [<] [popd] [pop] ifte
> > >
> > >
> > > In general, it makes sense for max/min to be defined on any type
> > that
> > > has a total order.
> >
> > i think what we have here is a failure to communictae. ;-)
>
> unfortunately, the link still seems to be down :-(
>
>
> > as in k above, so in conk below: a string is a list of characters:
> >
> > ['a 'b 'c 'd]
> > "abcd"
> > first
> > 'a
> >
> > more precisely, "abcdef" is "vector notation" for the list of length
> > six whose first element is the atom 'a, &c.
>
> Sure, Joy and Haskell do the same.
>
>
> >
> > since max and > are atomic operations, they require that their
> > operands
> > are conformable. that's why
> >
> > "abcd" 'b >
> > [1 0 0 0]
>
> That seems an overly restrictive definition of '>'. How are you going to
> sort strings? If the answer is by using a special string comparison
> function (ie. not '>') then my point stands: your code is less generic
> than it could be.
["first" "second" "third" "fourth"] up
[0 3 1 2]
>
>
> >
> > (remember - x y > means: y > x, not x > y).
>
> Yes, I remember you mentioning that conk works this way some time back,
> but I don't see that this has any relevance to genericity.
i mentioned it so that you wouldn't puzzle over the behavior.
>
>
> > and
> >
> > "abcd" 'b max
> > "bbcd"
>
> This is a different operation than returning the maximum of two values:
> its max applied to a list, ie. [max] cons map.
no - it's atomic maximumu, which applies to conformable lists.
I would therefore give
> it a different name. The reason is, if you want code to be both generic
> and readable, every name should have _one meaning_. Overloading the
> same name with different meanings is what gives overloading a bad name
> (no pun intended :-).
>
>
> --
> Louis.
>
> ------------------------------------------------------------------------
> Was the salesman clueless? Productopia has the answers.
> http://click.egroups.com/1/4633/7/_/_/_/962754978/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
Louis Madon — 2000-07-05 06:50:51
stevan apter wrote:
> From: Louis Madon <madonl@...>
> > stevan apter wrote:
> > > and
> > >
> > > "abcd" 'b max
> > > "bbcd"
> >
> > This is a different operation than returning the maximum of two values:
> > its max applied to a list, ie. [max] cons map.
>
> no - it's atomic maximumu, which applies to conformable lists.
We still seem to be on completely different wavelengths ...
here's my definition of max:
max : a a -- a
a binary function that takes two input values of _comparable_ types and
returns either one or the other, whichever was greater.
no matter how many types 'max' has implementions for I would expect this
meaning to be preserved. ie. one name, one meaning.
your definition of max has at least _two_ meanings. I presume k does not
support user-defined overloading, then this would be acceptable since
you can learn all the meanings laid down by the language designers once
and for all.
But a language that allows both user-defined overloads _and_ multiple
meanings per name is bad: to read such code you need to mentally connect
each name with a particular implementation (ie. disambiguate it) and
then refer to the implementation code to see what it actually does.
Moreover, someone could add a new overload at any time that could break
"tested" code.
If there is one name, one meaning, then the set of names in a program
form an unambiguous vocabulary, so for example:
splitAbove == [>] split
can be read and understood in isolation without worrying about specific
types or looking at the implementations of any of the functions used.
The price you pay (eg. in k) for not having user-defined overloading is
reduced genericity; it seems you can't write a generic sort or a generic
find-largest (ie. that can work on integers _and_ strings _and_ dates
_and_ whatever other types might be added in the future - as long as
they support > the generic code should not need to be changed).
--
Louis.
stevan apter — 2000-07-05 11:10:48
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Wednesday, July 05, 2000 2:50 AM
Subject: Re: [stack] three more proposals about Joy
> stevan apter wrote:
> > From: Louis Madon <madonl@...>
> > > stevan apter wrote:
> > > > and
> > > >
> > > > "abcd" 'b max
> > > > "bbcd"
> > >
> > > This is a different operation than returning the maximum of two values:
> > > its max applied to a list, ie. [max] cons map.
> >
> > no - it's atomic maximumu, which applies to conformable lists.
>
> We still seem to be on completely different wavelengths ...
>
> here's my definition of max:
>
> max : a a -- a
>
> a binary function that takes two input values of _comparable_ types and
> returns either one or the other, whichever was greater.
right, mine too. i don't know how you want to explain this, but in k
"abc" < "defghi"
length error
"abc" < "defghi"
^
do you want to say that two strings are not comparable, or not simply or
atomically comparable, or not "types"? or do you want to say that they
(as we do) that they are not conformable? but note that:
"abc" <\:/: "defghi" / less-than-eachleft-eachright
(1 1 1
1 1 1
1 1 1
1 1 1
1 1 1
1 1 1)
atomic functions encapsulate this geneeral "drill-down" behavior for
an infinite range of conformable structures.
on the other other hand, the two (atomic) symbols are:
`abc < `defghi
1
<, >, =, | (max), & (min) etc. are atomic functions. strings
are not atomic types. symbols are.
>
>
> no matter how many types 'max' has implementions for I would expect this
> meaning to be preserved. ie. one name, one meaning.
agreed.
>
> your definition of max has at least _two_ meanings.
it does? i don't think so. like any other atomic dyadic function, max
takes two conformable arguments and applies at the atoms.
> I presume k does not
> support user-defined overloading, then this would be acceptable since
> you can learn all the meanings laid down by the language designers once
> and for all.
right.
>
> But a language that allows both user-defined overloads _and_ multiple
> meanings per name is bad: to read such code you need to mentally connect
> each name with a particular implementation (ie. disambiguate it) and
> then refer to the implementation code to see what it actually does.
> Moreover, someone could add a new overload at any time that could break
> "tested" code.
i guess so. but as you say, we don't have user-defined types.
>
> If there is one name, one meaning, then the set of names in a program
> form an unambiguous vocabulary, so for example:
>
> splitAbove == [>] split
>
> can be read and understood in isolation without worrying about specific
> types or looking at the implementations of any of the functions used.
>
> The price you pay (eg. in k) for not having user-defined overloading is
> reduced genericity; it seems you can't write a generic sort or a generic
> find-largest (ie. that can work on integers _and_ strings _and_ dates
> _and_ whatever other types might be added in the future - as long as
> they support > the generic code should not need to be changed).
well, that's true. we've often wondered what the tradeoffs would be
if we provided the ability to introduce new types. e.g. currently we
represent dates as integers - say, 19990302. so we can't overload
+ and - to do date arithmetic. we have to write functions dateadd and
datesub. the fact that integer n in a program is a date is a fact
about the programmer's intentions, the type exists only in the programmer's
mind. the added value of having the interpreter know that n is a date
seems small, less than it might be in scalar languages. in all, an
open question.
>
>
> --
> Louis.
>
> ------------------------------------------------------------------------
> CLICK HERE AND START SAVING ON LONG DISTANCE BILLS TODAY!
> http://click.egroups.com/1/4125/7/_/_/_/962779556/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
Louis Madon — 2000-07-05 13:50:58
stevan apter wrote:
>
> ----- Original Message -----
> From: Louis Madon <madonl@...>
> To: <concatenative@egroups.com>
> Sent: Wednesday, July 05, 2000 2:50 AM
> Subject: Re: [stack] three more proposals about Joy
>
> > stevan apter wrote:
> > > From: Louis Madon <madonl@...>
> > > > stevan apter wrote:
> > > > > and
> > > > >
> > > > > "abcd" 'b max
> > > > > "bbcd"
> > > >
> > > > This is a different operation than returning the maximum of two values:
> > > > its max applied to a list, ie. [max] cons map.
> > >
> > > no - it's atomic maximumu, which applies to conformable lists.
> >
> > We still seem to be on completely different wavelengths ...
> >
> > here's my definition of max:
> >
> > max : a a -- a
> >
> > a binary function that takes two input values of _comparable_ types and
> > returns either one or the other, whichever was greater.
>
> right, mine too. i don't know how you want to explain this, but in k
>
> "abc" < "defghi"
> length error
> "abc" < "defghi"
> ^
Right, because as you say "atomic functions apply at the atoms" and so a
binary function needs an equal supply of atoms from each list argument.
> do you want to say that two strings are not comparable, or not simply or
> atomically comparable, or not "types"? or do you want to say that they
> (as we do) that they are not conformable?
> but note that:
>
> "abc" <\:/: "defghi" / less-than-eachleft-eachright
> (1 1 1
> 1 1 1
> 1 1 1
> 1 1 1
> 1 1 1
> 1 1 1)
>
> atomic functions encapsulate this geneeral "drill-down" behavior for
> an infinite range of conformable structures.
>
> on the other other hand, the two (atomic) symbols are:
>
> `abc < `defghi
> 1
>
> <, >, =, | (max), & (min) etc. are atomic functions. strings
> are not atomic types. symbols are.
Ok, all this makes sense. Now the way I would define the meaning of
'max' then is something like:
max : x y -- r
if x and y are atomic then return greater of x, y
else if x is a list and y is atomic then recursively apply (max y) to
elements of x
else ... etc for remaining x/y combos
> >
> > no matter how many types 'max' has implementions for I would expect
> this
> > meaning to be preserved. ie. one name, one meaning.
>
> agreed.
>
> >
> > your definition of max has at least _two_ meanings.
>
> it does? i don't think so. like any other atomic dyadic function,
> max takes two conformable arguments and applies at the atoms.
It does, but I need to clarify what I mean by "two". Whenever the
meaning assigned to a name involves type distinctions, I consider it to
have multiple meanings. For 'max' the behaviour clearly varies depending
on whether both arguments are atomic or not. To see why this is
significant, consider this example:
top-larger == dup [max] dip =
this function expects two comparable values on the stack and returns
True if the top-of-stack value is larger. In conk this code can only be
guaranteed to work for atomic values. It breaks otherwise, because the
behaviour of max changes in the presence of list arguments such that you
can no longer assume equality between one of the inputs and the result.
Code that works fine initially but then fails (basically does something
unexpected) when new types come along is a symptom of having multiple
meanings - the new meanings are inconsistent with prior understanding.
Now in k, as I've said, this isn't really an issue, since the user can't
introduce new types nor modify the meanings of existing types nor add
any new overloads.
But in a language that does support type definition and overloading, the
code can easily become very confusing and fragile if the "one name, one
meaning" principle isn't followed. Basically, any properties required
by some generic code for one specific type must also apply to _all_
other types that may be given to that code. This in turn makes it
possible to read the code without even thinking about specific types
(you just interpret the names - they each have one meaning so there is
no ambiguity).
>
> > I presume k does not support user-defined overloading, then this would be acceptable
> since you can learn all the meanings laid down by the language designers
> once and for all.
>
> right.
>
> >
> > But a language that allows both user-defined overloads _and_ multiple
> > meanings per name is bad: to read such code you need to mentally connect
> > each name with a particular implementation (ie. disambiguate it) and
> > then refer to the implementation code to see what it actually does.
> > Moreover, someone could add a new overload at any time that could break
> > "tested" code.
>
> i guess so. but as you say, we don't have user-defined types.
>
> >
> > If there is one name, one meaning, then the set of names in a program
> > form an unambiguous vocabulary, so for example:
> >
> > splitAbove == [>] split
> >
> > can be read and understood in isolation without worrying about
> specific
> > types or looking at the implementations of any of the functions
> used.
> >
> > The price you pay (eg. in k) for not having user-defined overloading
> is
> > reduced genericity; it seems you can't write a generic sort or a
> generic
> > find-largest (ie. that can work on integers _and_ strings _and_
> dates
> > _and_ whatever other types might be added in the future - as long as
> > they support > the generic code should not need to be changed).
>
> well, that's true. we've often wondered what the tradeoffs would be
> if we provided the ability to introduce new types. e.g. currently we
> represent dates as integers - say, 19990302. so we can't overload
> + and - to do date arithmetic. we have to write functions dateadd and
> datesub. the fact that integer n in a program is a date is a fact
> about the programmer's intentions, the type exists only in the
> programmer's
> mind. the added value of having the interpreter know that n is a date
> seems small, less than it might be in scalar languages. in all, an
> open question.
Yes, thats a reasonable position. I'm interested in highly generic code
for benefits of conciseness, re-usability and quick modifiability (which
ultimately could mean much higher productivity). Its what I'm working on
in my version of Joy and time will tell what merit my approach really
has.
--
Louis.
sa@dfa.com — 2000-07-05 14:18:24
stevan apter wrote:
>
> ----- Original Message -----
> From: Louis Madon <madonl@...>
> To: <concatenative@egroups.com>
> Sent: Wednesday, July 05, 2000 2:50 AM
> Subject: Re: [stack] three more proposals about Joy
>
> > stevan apter wrote:
> > > From: Louis Madon <madonl@...>
> > > > stevan apter wrote:
> > > > > and
> > > > >
> > > > > "abcd" 'b max
> > > > > "bbcd"
> > > >
> > > > This is a different operation than returning the maximum of two
values:
> > > > its max applied to a list, ie. [max] cons map.
> > >
> > > no - it's atomic maximumu, which applies to conformable lists.
> >
> > We still seem to be on completely different wavelengths ...
> >
> > here's my definition of max:
> >
> > max : a a -- a
> >
> > a binary function that takes two input values of _comparable_ types and
> > returns either one or the other, whichever was greater.
>
> right, mine too. i don't know how you want to explain this, but in k
>
> "abc" < "defghi"
> length error
> "abc" < "defghi"
> ^
Right, because as you say "atomic functions apply at the atoms" and so a
binary function needs an equal supply of atoms from each list argument.
that's right if you think of f(atom,list) as first replicating
the atom count-list-many times (and doing so recursively). but
remember that e.g. a list of three atoms and a list of three lists
of any structure are conformable. &c. so e.g. a 5 x 4 x 6 x 3
list can be added to a 5 x 4 list, yielding a 5 x 4 x 6 x 3 list.
> do you want to say that two strings are not comparable, or not simply or
> atomically comparable, or not "types"? or do you want to say that they
> (as we do) that they are not conformable?
> but note that:
>
> "abc" <\:/: "defghi" / less-than-eachleft-eachright
> (1 1 1
> 1 1 1
> 1 1 1
> 1 1 1
> 1 1 1
> 1 1 1)
>
> atomic functions encapsulate this geneeral "drill-down" behavior for
> an infinite range of conformable structures.
>
> on the other other hand, the two (atomic) symbols are:
>
> `abc < `defghi
> 1
>
> <, >, =, | (max), & (min) etc. are atomic functions. strings
> are not atomic types. symbols are.
Ok, all this makes sense. Now the way I would define the meaning of
'max' then is something like:
max : x y -- r
if x and y are atomic then return greater of x, y
else if x is a list and y is atomic then recursively apply (max y) to
elements of x
else ... etc for remaining x/y combos
ok - we just say "max is atomic", since the atomic penetration algorithm
(elsewhere called "scalar extension") is the same for all of them.
> >
> > no matter how many types 'max' has implementions for I would expect
> this
> > meaning to be preserved. ie. one name, one meaning.
>
> agreed.
>
> >
> > your definition of max has at least _two_ meanings.
>
> it does? i don't think so. like any other atomic dyadic function,
> max takes two conformable arguments and applies at the atoms.
It does, but I need to clarify what I mean by "two". Whenever the
meaning assigned to a name involves type distinctions, I consider it to
have multiple meanings. For 'max' the behaviour clearly varies depending
on whether both arguments are atomic or not. To see why this is
significant, consider this example:
top-larger == dup [max] dip =
this function expects two comparable values on the stack and returns
True if the top-of-stack value is larger. In conk this code can only be
guaranteed to work for atomic values. It breaks otherwise, because the
behaviour of max changes in the presence of list arguments such that you
can no longer assume equality between one of the inputs and the result.
it breaks whenever the arguments are not conformable.
in conk, top_larger will return a boolean result determined
by the shapes of its arguments:
5 10 draw
[3 0 4 3 1 4 4 3 3 1]
5 10 draw
[3 0 4 3 1 4 4 3 3 1] [1 2 2 4 4 3 0 3 4 1]
dup [max] dip =
[0 1 0 1 1 0 0 1 1 1]
Code that works fine initially but then fails (basically does something
unexpected) when new types come along is a symptom of having multiple
meanings - the new meanings are inconsistent with prior understanding.
Now in k, as I've said, this isn't really an issue, since the user can't
introduce new types nor modify the meanings of existing types nor add
any new overloads.
however, if we had such a facility, then the new types would
be atomic. so atomic extension would apply.
But in a language that does support type definition and overloading, the
code can easily become very confusing and fragile if the "one name, one
meaning" principle isn't followed. Basically, any properties required
by some generic code for one specific type must also apply to _all_
other types that may be given to that code. This in turn makes it
possible to read the code without even thinking about specific types
(you just interpret the names - they each have one meaning so there is
no ambiguity).
ok
>
> > I presume k does not support user-defined overloading, then this would
be acceptable
> since you can learn all the meanings laid down by the language designers
> once and for all.
>
> right.
>
> >
> > But a language that allows both user-defined overloads _and_ multiple
> > meanings per name is bad: to read such code you need to mentally
connect
> > each name with a particular implementation (ie. disambiguate it) and
> > then refer to the implementation code to see what it actually does.
> > Moreover, someone could add a new overload at any time that could break
> > "tested" code.
>
> i guess so. but as you say, we don't have user-defined types.
>
> >
> > If there is one name, one meaning, then the set of names in a program
> > form an unambiguous vocabulary, so for example:
> >
> > splitAbove == [>] split
> >
> > can be read and understood in isolation without worrying about
> specific
> > types or looking at the implementations of any of the functions
> used.
> >
> > The price you pay (eg. in k) for not having user-defined overloading
> is
> > reduced genericity; it seems you can't write a generic sort or a
> generic
> > find-largest (ie. that can work on integers _and_ strings _and_
> dates
> > _and_ whatever other types might be added in the future - as long as
> > they support > the generic code should not need to be changed).
>
> well, that's true. we've often wondered what the tradeoffs would be
> if we provided the ability to introduce new types. e.g. currently we
> represent dates as integers - say, 19990302. so we can't overload
> + and - to do date arithmetic. we have to write functions dateadd and
> datesub. the fact that integer n in a program is a date is a fact
> about the programmer's intentions, the type exists only in the
> programmer's
> mind. the added value of having the interpreter know that n is a date
> seems small, less than it might be in scalar languages. in all, an
> open question.
Yes, thats a reasonable position. I'm interested in highly generic code
for benefits of conciseness, re-usability and quick modifiability (which
ultimately could mean much higher productivity). Its what I'm working on
in my version of Joy and time will tell what merit my approach really
has.
naturally, i am eager to see how this shakes out.
--
Louis.
------------------------------------------------------------------------
Free, Unlimited Calls Anywhere!
Conference in the whole family on the same call.
Let the fights begin! Visit Firetalk.com - Click below.
http://click.egroups.com/1/5476/7/_/_/_/962804739/
------------------------------------------------------------------------
To unsubscribe from this group, send an email to:
concatenative-unsubscribe@egroups.com
Louis Madon — 2000-07-05 22:43:43
sa@... wrote:
> top-larger == dup [max] dip =
>
> this function expects two comparable values on the stack and returns
> True if the top-of-stack value is larger. In conk this code can only be
> guaranteed to work for atomic values. It breaks otherwise, because the
> behaviour of max changes in the presence of list arguments such that you
> can no longer assume equality between one of the inputs and the
> result.
>
> it breaks whenever the arguments are not conformable.
> in conk, top_larger will return a boolean result determined
> by the shapes of its arguments:
>
> 5 10 draw
> [3 0 4 3 1 4 4 3 3 1]
> 5 10 draw
> [3 0 4 3 1 4 4 3 3 1] [1 2 2 4 4 3 0 3 4 1]
> dup [max] dip =
> [0 1 0 1 1 0 0 1 1 1]
Ok, so what does this do:
top-larger "its larger" "its smaller" branch
When the values are both atomic it works fine, but in the presence of
list arguments - even if they're conforming - it breaks, correct?
--
Louis.
Louis Madon — 2000-07-05 23:21:23
Louis Madon wrote:
>
> sa@... wrote:
>
> > top-larger == dup [max] dip =
> >
> > this function expects two comparable values on the stack and returns
> > True if the top-of-stack value is larger. In conk this code can
> only be
> > guaranteed to work for atomic values. It breaks otherwise, because
> the
> > behaviour of max changes in the presence of list arguments such that
> you
> > can no longer assume equality between one of the inputs and the
> > result.
> >
> > it breaks whenever the arguments are not conformable.
> > in conk, top_larger will return a boolean result determined
> > by the shapes of its arguments:
> >
> > 5 10 draw
> > [3 0 4 3 1 4 4 3 3 1]
> > 5 10 draw
> > [3 0 4 3 1 4 4 3 3 1] [1 2 2 4 4 3 0 3 4 1]
> > dup [max] dip =
> > [0 1 0 1 1 0 0 1 1 1]
>
> Ok, so what does this do:
>
> top-larger "its larger" "its smaller" branch
>
> When the values are both atomic it works fine, but in the presence of
> list arguments - even if they're conforming - it breaks, correct?
Then again, on reflection, I think it would return (eg.)
["its smaller" "its smaller" "its larger" ... etc]
So perhaps it is possible to view the behaviour as having a single
meaning (independant of specific types), though a bit different to what
I'm used to.
It is interesting.
--
Louis.
stevan apter — 2000-07-06 01:22:39
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Wednesday, July 05, 2000 6:43 PM
Subject: Re: [stack] three more proposals about Joy
> Ok, so what does this do:
>
> top-larger "its larger" "its smaller" branch
>
> When the values are both atomic it works fine, but in the presence of
> list arguments - even if they're conforming - it breaks, correct?
assuming branch requires an atom, yes.
>
>
> --
> Louis.
>
> ------------------------------------------------------------------------
> Who invented Gatorade -- and what part did it play in
> winning the1967 Orange Bowl? Find out the true facts at
> http://click.egroups.com/1/6212/7/_/_/_/962836699/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
stevan apter — 2000-07-06 01:40:47
----- Original Message -----
From: Louis Madon <madonl@...>
To: <concatenative@egroups.com>
Sent: Wednesday, July 05, 2000 7:21 PM
Subject: Re: [stack] three more proposals about Joy
> >
> > Ok, so what does this do:
> >
> > top-larger "its larger" "its smaller" branch
> >
> > When the values are both atomic it works fine, but in the presence of
> > list arguments - even if they're conforming - it breaks, correct?
>
> Then again, on reflection, I think it would return (eg.)
>
> ["its smaller" "its smaller" "its larger" ... etc]
oh, *that* branch. ;-)
well, here's what conk does:
[1 0 1 0 1] "true" "false" branch
"true" "false" "true" "false" "true"
and here's how it's implemented:
branch:{(3_ x)E/x x 2}
the key is x (the stack) indexed by x 2 (the boolean, of whatever
shape or complexity). so yes, even if the boolean is some deeply
structured list, the result will inherit that structure. minus its
outer frame:
[[1 0 1 0][0 1 0]] "true "false" branch
["true" "false" "true" "false"] ["false" "true" "false"]
[1 0 1] enlist [2 3] take
[[[1 0 1] [1 0 1] [1 0 1]] [[1 0 1] [1 0 1] [1 0 1]]]
"true" "false" branch
[["true" "false" "true"] ["true" "false" "true"] ["true" "false" "true"]] [["tr
e" "false" "true"] ["true" "false" "true"] ["true" "false" "true"]]
>
>
> So perhaps it is possible to view the behaviour as having a single
> meaning (independant of specific types), though a bit different to what
> I'm used to.
>
> It is interesting.
>
>
> --
> Louis.
>
> ------------------------------------------------------------------------
> Who invented Gatorade -- and what part did it play in
> winning the1967 Orange Bowl? Find out the true facts at
> http://click.egroups.com/1/6212/7/_/_/_/962839412/
> ------------------------------------------------------------------------
>
> To unsubscribe from this group, send an email to:
> concatenative-unsubscribe@egroups.com
>
>
>
>
wtanksley@bigfoot.com — 2000-08-01 18:34:47
From:
iepos@... [mailto:
iepos@...]
>Hello folks ...
>The last while, I've been thinking a bit about a few ways I would like
>Joy to be modified. You may or may not like these ideas ...
>1) Design the system so that the user feels free to pile tons of items
> onto the stack (treating it as an array). Modify the "size"
> primitive
> to tell how many items are on the stack. Add a primitive "wrap"
> that wraps the whole stack up into a list.
This type of operation does have its uses, but its biggest shortcoming is
that it demands absolute knowledge of the stack. It essentially treats the
stack as a global variable. This is just fine if you're expecting that (so
there's no problem with including words which have those actions), but it's
wrong to build the language around such a dangerous premise.
>2) Eliminate the ability to do direct introspection on quotations.
> This is actually a consequence of the suggestion in #1;
> "first", "rest", "at", "take", "size" could no longer be used to
> peek into a quotation, since the reformed versions fiddle with items
> directly on the stack instead of in quotations.
My solution for this is to eliminate the ability to directly execute a
quotation; instead, you can execute only functions. Fortunately, functions
may be compiled from quotations. (And, of course, we can define a word
which compiles a quotation anonymously and executes the resulting function
pointer.)
>3) Replace Joy's "==" construct, and its friends, "LIBRA",
> "HIDE", "IN",
> and "END" with a fully-fledged lambda construct. (Okay, I've already
> mentioned this before).
Very bad idea, especially since your definition of "fully-fledged" also
requires special cases for quotation (for example, [x] would push x on the
stack, instead of pushing [x] on the stack, unless x is multiple elements).
Also, lambda by itself cannot handle exports; a lambda name endures only as
long as its scope does.
It would certainly be possible to define a pair of definition constructs,
though:
[5 4 +] lambda: x
5 4 + constant: y
x --> 9
y --> 9
These definitions could be valid until the end of the scope in which they
were defined. Of course, you'd lose some of the nice properties of Joy's
current naming system, in that naming errors wouldn't be known until
runtime.
An interesting problem: I don't see any way to handle quotation here. If
you quote x or y, it seems that you've just quoted something which may be
transient. Quoting x is particularly hard; here's what it might look like:
[x] --> [9]
or
[x] --> [5 4 +]
The problem is clear: the lambda reference has to execute even in the middle
of a quotation. Now, this is a handy ability, so rather than choosing one
possible operation I'm going to choose both.
"5 4 +" text-macro: z
[5 4 +] macro: w
[5 4 +] lambda: x
All of these look the same when used unquoted. When quoted, though:
[x] --> [[5 4 +] i]
[z] --> [5 4 +]
[w] --> [9]
'macro:' can be used to define all of the others, because it simply
specifies execution at compile-time. However, we would require a way to
distinguish execution within a quotation from execution outside of a
quotation in order to do that correctly.
I'm not going to continue; this has far surpassed my tolerance for language
cruft.
>- "iepos" (Brent Kerby)
-Billy
iepos@tunes.org — 2000-08-03 18:07:11
iepos@tunes.org — 2000-08-04 14:52:02
Sorry about the blank message yesterday ...
not sure quite how that happened.
> >1) Design the system so that the user feels free to pile tons of items
> > onto the stack (treating it as an array). Modify the "size"
> > primitive
> > to tell how many items are on the stack. Add a primitive "wrap"
> > that wraps the whole stack up into a list.
>
> This type of operation does have its uses, but its biggest shortcoming is
> that it demands absolute knowledge of the stack. It essentially treats the
> stack as a global variable. This is just fine if you're expecting that (so
> there's no problem with including words which have those actions), but it's
> wrong to build the language around such a dangerous premise.
Yes. there are some disadvantages to this style. It might not be fit
in some systems. I still like the idea, though.
> >2) Eliminate the ability to do direct introspection on quotations.
> > This is actually a consequence of the suggestion in #1;
> > "first", "rest", "at", "take", "size" could no longer be used to
> > peek into a quotation, since the reformed versions fiddle with items
> > directly on the stack instead of in quotations.
>
> My solution for this is to eliminate the ability to directly execute a
> quotation; instead, you can execute only functions. Fortunately, functions
> may be compiled from quotations. (And, of course, we can define a word
> which compiles a quotation anonymously and executes the resulting function
> pointer.)
Hmm. Let me get this straight... By "quotation", you mean a list of
execution tokens, a high-level representation of a program. By "function",
you mean a list of native instruction bytes. Is that right?
> It would certainly be possible to define a pair of definition constructs,
> though:
>
> [5 4 +] lambda: x
> 5 4 + constant: y
>
> x --> 9
> y --> 9
Yes, I believe it would be useful to have both "lambda:" and "constant:",
as you suggest. However, it is possible to construct "constant:" as
"unitlist lambda:". "unitlist" takes an item off the stack and converts
it into a _program that pushes that item_, so that it is a valid input
to "lambda:" (which only accepts programs, and nothing else).
> The problem is clear: the lambda reference has to execute even in the middle
> of a quotation. Now, this is a handy ability, so rather than choosing one
> possible operation I'm going to choose both.
>
> "5 4 +" text-macro: z
> [5 4 +] macro: w
> [5 4 +] lambda: x
>
> All of these look the same when used unquoted. When quoted, though:
> [...]
Lambdas are confusing, and there are many ways of doing them, as you say.
I'm not sure exactly what the best way is ...
> -Billy
- "iepos" (Brent Kerby)
wtanksley@bigfoot.com — 2000-08-04 23:08:42
From:
iepos@... [mailto:
iepos@...]
[treating the entire stack as an object]
>> This type of operation does have its uses, but its biggest
>> shortcoming is
>> that it demands absolute knowledge of the stack. It
>> essentially treats the
>> stack as a global variable. This is just fine if you're
>> expecting that (so
>> there's no problem with including words which have those
>> actions), but it's
>> wrong to build the language around such a dangerous premise.
>Yes. there are some disadvantages to this style. It might not be fit
>in some systems. I still like the idea, though.
I don't, but I do like some ideas which are related to it. For example, I
believe an explicitly finite stack can be useful. Chuck Moore's programs
use a 18-deep stack without problems.
When it comes right down to it, there's no such thing as an infinitely deep
stack; so it's better to have a small stack whose size you know exactly than
to have a huge stack which you mistakenly pretend is infinite.
>> >2) Eliminate the ability to do direct introspection on quotations.
>> > This is actually a consequence of the suggestion in #1;
>> > "first", "rest", "at", "take", "size" could no longer be used to
>> > peek into a quotation, since the reformed versions
>> > fiddle with items
>> > directly on the stack instead of in quotations.
>> My solution for this is to eliminate the ability to directly
>> execute a
>> quotation; instead, you can execute only functions.
>> Fortunately, functions
>> may be compiled from quotations. (And, of course, we can
>> define a word which compiles a quotation anonymously and
>> executes the resulting function pointer.)
>Hmm. Let me get this straight... By "quotation", you mean a list of
>execution tokens, a high-level representation of a program.
Yes.
>By "function",
>you mean a list of native instruction bytes. Is that right?
Um... Not really. A function has no structure, so it's not a "list" of
native instruction bytes. Now, it might be implemented that way; but I
offer no guarantees.
>> The problem is clear: the lambda reference has to execute
>> even in the middle
>> of a quotation. Now, this is a handy ability, so rather
>> than choosing one possible operation I'm going to choose
>> both.
>> "5 4 +" text-macro: z
>> [5 4 +] macro: w
>> [5 4 +] lambda: x
>> All of these look the same when used unquoted. When quoted, though:
>> [...]
>Lambdas are confusing, and there are many ways of doing them,
>as you say. I'm not sure exactly what the best way is ...
IMO, the best thing to do is to avoid them like the plague that they are.
They're TOTALLY unneeded, and completely change the rules of the language in
a way which doesn't make sense.
Both text macros and macros are useful, though.
Of course, I'm opinionated. :-) I also don't really like Joy's quotation
literals; they help introduce many of the ambiguities encountered by
lambdas. They also change Joy's perfect list structure into a tree
structure, which has resulted in many people confusing Joy with Scheme.
>- "iepos" (Brent Kerby)
-Billy