Re: [stack] Joy in Avail

stevan apter — 2000-06-03 11:25:30

> I believe the K implementation Stevan Apter <apter@...> submitted
> earlier is more concise and much more comprehensive (neither feature was a
> goal for me at this time). On the other hand, it required the Joy
> expressions to be quoted (via 'I"'). I may have misunderstood the K code,
> but it seemed that some Joy operations were defined via 'I"' quoted Joy
> expresions, but others were defined via K code directly. I didn't see any
> mixtures, and I don't understand the implementation well enough to hazard a
> guess whether it would be possble to mix the Joy and K code in a definition.
>

hi mark

i think i can answer some of your questions.

the core of my joy interpreter is the set of functions:

E:{:[~4=4:y;(,y),x;7=4:v:. y;v x;6=4:v;(,$y),x;x _f/v]}
X:()E/
Y:()E\
O:Parse.out
I:Parse.in
R:{`0:O r:x E/y;r}
S:{`0:,/O'r:x E\y;*|r}
J:{s:();o:(R;S);while[#r:{`0:"joy>";0:`}[];:["\\"=*r;o|:;s:(*o)[s;I r]]]}

J starts the joy console. while there is input r, let the stack (s) be R
of the stack and the parse of the input. i.e. s:R[s;I r].

R is defined as the stack (x) evaluated (E) over the input (y). a side
effect of R is to print (`0:) the new stack at the console.

E is the evaluator: if the input is not a symbol (~4=4:y) then prepend
it to the stack, else if the evaluation of the input is a function (7=4:
v:. y) then apply it to the stack, else if the evaluated input is null
(6=4:v) then prepend the format of the input to the stack, else the
evaluated input must be a list of some form (i.e. a joy program), so
recursively evaluate it (x _f/v).

X and Y are two ways to recursively evaluate the stack. remember that
in my implementation, the stack is a list s. ()E/s takes any stack and
reduces it to a single evaluated item. ()E\s takes an stack, reduces
it, and returns a list each of whose items is the stack reduction up
to that point. X and Y are designed to be used directly, in k (see
below).

O and I currently point to my quick and dirty joy parser/unparser. but
you can swap in any syntax you like, merely by redefining Parse.out and
Parse.in.

my goal was to be able to implement joy primitives as k functions. for
example, the basic stack functions are:

id:(::) / identity returns the stack
false:{0,x} / put 0 on the stack
true:{1,x} / put 1 on the stack
stack:{(,x),x} / put the enlisted stack on the stack
newstack:{x;()} / return the empty stack
unstack:{x[0],1_ x} / unlist the top of the stack
pop:1_ / drop the top of the stack
dup:{x[,0],x} / prepend the enlisted top of the stack
swap:{@[x;1 0;:;2#x]} / reverse the top 2 in place
rollup:{x[1 2 0],3_ x} / etc.
rolldown:{x[2 0 1],3_ x}
choice:{(x[0 1]x 3),3_ x}

now to your question: i defined e.g. swapd as:

I"[swap]dip"

as the parse of the joy concatenation [swap]dip. the resulting
k list is

(,`swap;`dip)

a two item list, whose first item is a one-item list of the symbol
`swap and whose second item is the symbol `dip. i use I because
it allows k to define joy programs as lists more consisely than
the primitive list notation of k.

a more satisfying implementation of joy would attempt to define as
many of the joy operators as possible this way, i.e. as lists rather
than k functions.

in brief, there are three ways to use the k implementation of joy:

as an interactive interpreter via J[]

as a sublanguage within k, using I, O, and X (or Y):

X I"1 2 +"
,3

Y I"1 2 +"
(()
,1
2 1
,3)

as a set of subfunctions, each of which takes a stack, i.e.,

plus 1 2
,3

hope this helps (a revised version is included below - i still
haven't found the time to complete the implementation)

--

/ joy in k

/ joy <-> k (quick & dirty)

\d Parse
types:{:[4:x;type x;_f'x]}
type:{:[(*x)_in*Ops;Ops[1;Ops[0]?*x];&/x _lin"_.0123456789";.@[x;&x="_";:;"-"];"\""=*x;1_-1_ x;"'"=*x;x 1;`$x]}
parts:{a:ol x;b:"["=*:'a;a[&b],:;a:@[a;&~b;ob];r:(,/a)_dv"";@[r;&"["=*:'r;_f 1_-1_]}
sa:{,/@[x;&uq[x]&x=y;:[;" ",y," "]]}
ol:{(&1(~=)':ls x)_ x}
ob:{1_'(&uq[x]&x=" ")_ x:" ",x}
uq:{~(0<':b)|b:(~=)\(~(!#x)_lin,/0 1+/:x _ss"\\\"")&x="\""}
ls:{~|':~=/+\'uq[" ",x]&/:(" ",x)=/:"[]"}
nl:{@[x;&x _lin"\n\t";:;" "]}
vm:{:[4:x;x,"";1_,/" ",'x]}
in:{types parts(nl vm x)sa/"[]",*Ops}
out:{(1_,/(" ",term@)'|x),"\n"}
term:{:[-3=t:4:x;"\"",x,"\"";1>t;list@_f'x;t=3;"'",x;t=5;_f(+($!x;x[]);`dict);$(Ops[0],,x)Ops[1]?x]}
list:{"[",(1_,/" ",'$x),"]"}
Ops:("&|~+-*/%=<>";`and`or`not`plus`minus`mul`idiv`div`eq`lt`gt)
\d ^

/ eval, console, &c.

E:{:[~4=4:y;(,y),x;7=4:v:. y;v x;6=4:v;(,$y),x;x _f/v]}
X:()E/
Y:()E\
O:Parse.out
I:Parse.in
R:{`0:O r:x E/y;r}
S:{`0:,/O'r:x E\y;*|r}
J:{s:();o:(R;S);while[#r:{`0:"joy>";0:`}[];:["\\"=*r;o|:;s:(*o)[s;I r]]]}

/ utilities

Two:{(,Dot[x;2#y]),2 _ y}
One:{@[y;0;x]}
Dot:{:[-3=4:y;.($x),"[",(1_,/";",'"\"",'y,'"\""),"]";x . y]}
Num:{(_ci;::)[(b~z)&a~y]x[b:Ic z;a:Ic y]}
Ic:{:[3=4:*x;_ic x;x]}
Rel:{x[Ic z;Ic y]}
Each:{y{*(,x)E/y}\:x}
Ary:{((1_ y)E/*y),(x+1)_ y}
App:{(((x#1_ y),\:(x+1)_ y)E/\:*y),(x+1)_ y}
Dip:{(x#1_ y),((1+x)_ y)E/*y}
Type:{:[4=4:x;x;~4:x;();-3=4:x;"";*0#x]}
Leaf:{(&/@:'x)|(@x)|-3=4:x}

/ assignment

is:Two[{.[`$x;();:;y]}]

/ general operators

id:(::)
false:{0,x}
true:{1,x}
stack:{(,x),x}
newstack:{x;()}
unstack:{x[0],1_ x}
pop:1_
dup:{x[,0],x}
swap:{@[x;1 0;:;2#x]}
rollup:{x[1 2 0],3_ x}
rolldown:{x[2 0 1],3_ x}
choice:{(x[0 1]x 3),3_ x}
opcase:Two[{((1_'-1_ x),,*|x)((Type@*:)'-1_ x)?Type@*y}]
swapd:I"[swap]dip"
dupd:I"[dup]dip"
popd:I"[pop]dip"
popop:I"pop pop"

/ i/o

put:{}
get:{}
load:{(1_ x)E/I@0:x[0],".joy"}

/ atomic operators

max:or:Two[Num[|]]
min:and:Two[Num[&]]
not:One[~:]
neg:One[-:]
plus:Two[Num[+]]
minus:Two[Num[-]]
mul:Two[*]
div:Two[{y%x}]
rem:Two[{y!x}]
flr:One[_:]
idiv:I"div flr"
sign:One[{(-x<0)+0<x}]
abs:One[_abs]
pred:One[Num[+;-1]]
succ:One[Num[+;1]]

/ list operators

rand:Two[{y _draw x}]
enum:One[!:]
cut:Two[{y _ x}]
find:Two[{y?x}]
up:One[<:]
down:One[>:]
sub:Two[{y@x}]
upsort:I"up sub"
downsort:I"down sub"
cons:Two[{(,y),x}]
swons:I"swap cons"
first:One[*:]
second:One[{x 1}]
third:One[{x 2}]
last:One[*|:]
rest:One[1_]
of:Two[{x:[1=4:y;y;`$y]}]
at:I"swap of"
drop:Two[_]
take:Two[#]
size:One[#:]
reverse:One[|:]
uncons:{((1_;*:)@\:*x),1_ x}
unswons:I"uncons swap"
flatten:One[,/]
transpose:One[+:]
concat:Two[{y,x}]
swoncat:Two[,]
transpose:One[+:]
dict:One[{..[x;(;0);`$]}]

/ predicates

null:One[{:[@x;x=0;~#x]}]
small:One[{:[@x;x<2;2>#x]}]
gt:Two[Rel[>]]
lt:Two[Rel[<]]
eq:Two[Rel[=]]
le:Two[Rel[~>]]
ge:Two[Rel[~<]]
ne:Two[Rel[~=]]
equal:Two[~]
has:Two[_in]
in:Two[{y _in x}]
hasl:Two[_lin]
lin:Two[{y _lin x}]

/ general combinators

i:{(1_ x)E/*x}
x:{x E/*x}
y:{}
b:{((2_ x)E/x 1)E/*x}
nullary:Ary[0]
unary:Ary[1]
binary:Ary[2]
ternary:Ary[3]
dip:Dip[1]
dipd:Dip[2]
dipdd:Dip[3]
app1:App[1]
app2:App[2]
app3:App[3]
app4:App[4]
cleave:{((2_ x){*x E/y}/:2#x),3_ x}
construct:{}
ifte:{((3_ x)E/x@~~*(3_ x)E/x 2),4_ x}
cond:{c:*x;s:1_ x;while[1<#c;if[*s E/*c;:s E/c 1];c:2_ c]}
whiledo:{s:2_ x;while[*s:s E/x 1;s:s E/*x];s}
primrec:{:[x 2;(x[2],_f[@[x;2;-;1]])E/*x;(3_ x)E/x 1]}
linrec:{:[*(4_ x)E/x 3;(4_ x)E/x 2;(_f(4#x),(4_ x)E/x 1)E/*x]}
tailrec:{:[*(3_ x)E/x 2;(3_ x)E/x 1;_f(3#x),(3_ x)E/*x]}
binrec:{:[*(4_ x)E/x 3;(4_ x)E/x 2;((*_f@)'x{@[x;4;:;y]}/:2#(4_ x)E/x 1)E/*x]} / ?
genrec:{:[*(4_ x)E/x 3;(4_ x)E/x 2;(`genrec,(4#x),(4_ x)E/x 1)E/*x]}
condlinrec:{}

/ atomic combinators

branch:{((3_ x)E/x@~~z),4_ x}
times:{x[0]{y E/x}[x 1]/2_ x}

/ list combinators

infra:{(,x[1]E/*x),2_ x}
step:{{((,z),y)E/x}[*x]/[2_ x;x 1]}
map:{(,(1_'@[x;1;:;]'x 1){*x E/y}\:*x),2_ x}
fold:{(,{*(z;y)E/x}[*x]/[x 1;x 2]),3_ x}
filter:{1_@[x;1;@[;&*map x]]}
split:{((x[1]@&:;x[1]@&~:)@\:*map x),2_ x}
some:{@[map x;0;|/]}
all:{@[map x;0;&/]}

/ tree combinators

treestep:{:[Leaf x 1;step x;(,(*_f@)'x[1]{@[y;1;:;x]}\:x),2_ x]}
treerec:{:[Leaf x 2;(2_ x)E/x 1;((,`treerec,2#x),(2_ x))E/*x]}
treegenrec:{}

/ test routines

unitlist:I"[]cons"
shunt:I"[swons]step"
reverselist1:I"[]swap shunt"
reverselist2:I"[]swap infra"
restlist:I"[null][unitlist][dup rest][cons]linrec"
frontlist:I"[null][unitlist][uncons][[cons]map popd dup first rest swons]linrec"
subseqlist1:I"frontlist[restlist]map"
subseqlist2:I"[null][unitlist][uncons dup[frontlist[cons]map popd]dip][concat]linrec"
powerlist1:I"[null] [[] cons] [uncons][dup swapd [cons] map popd concat] linrec"
powerlist2:I"[null] [[] cons] [uncons][dup swapd [cons] map popd swoncat] linrec"
treesum:I"0 swap[+]treestep"
treesize:I"0 swap[pop succ]treestep"
treeshunt:I"[swons]treestep"
treeflatten:I"[]swap treeshunt reverse"

qsort:I"[small] [] [uncons [>] split] [swapd dip cons concat] binrec"

J[]

stevan apter — 2000-06-03 11:39:43

> I didn't see any
> mixtures, and I don't understand the implementation well enough to hazard a
> guess whether it would be possble to mix the Joy and K code in a definition.

good question (which i neglected to answer!)

yes, any k function can use I, since it's just another k function, but i can't
think of a situtation where i would want to do that.

one reason i abandoned work on the interpreter is that it doesn't conform strictly
enough to manfred's definition of the language. in part, this was because i didn't
(don't) understand how metasyntax like "HIDE", "IN", and "==" work.

by the way, has anyone here been in communication with manfred? (i've been
assuming that he's not on the mailing list.) is he continuing work on joy?
(the joy section of his website doesn't seem to have been updated in quite
a while).

i've been reading up on the subject of continuations (thank you christian
tismer) and it seems as though first-class continuations would be relatively
simple to implement in joy. any thoughts on this one?

wtanksley@bigfoot.com — 2000-06-05 16:54:46

From: stevan apter [mailto:apter@...]

>by the way, has anyone here been in communication with
>manfred? (i've been
>assuming that he's not on the mailing list.) is he continuing
>work on joy?
>(the joy section of his website doesn't seem to have been
>updated in quite a while).

He's working on a book on it, and he's subscribed to the list.

-Billy