Rabbit-vm

— 2014-05-05 04:21:52

Introducing the Rabbit-vm
==================

Some time ago I made another attempt to lern stack-based, concatenative or tacit style of programming.
Joy has no define-primitive. I would like to use () instead of [].
Factor, with its 224MB-installation seems a lot more than i can chew.

So the Rabbit-vm came into existence.

I would like to introduce a few things i found on my way.




a teaspoon of syntactic sugar
==================


() (1 2 3)  uncons (swons)dip
() (1 2 3)  uncons swons^

This ^ as a shortcut for ()dip is, IMHO of great help for writing, and mutch more for reading code.
Rabbit has up to ^^^^, only for words, not for quotations. I think that works out very well.


(name)
`name

This is a small difference. The improvement is not in typing but in reading: `name is most of the time a name, passed to a function.


(name)first
,name

This is sometimes helpful, sometimes a small pitfall:
Construct a quotation that executes doc with name

(1)    ,name (doc) cons i
(2)    (name) first (doc) cons i
(3)    name (doc) cons i
(4)    (name doc) i
(5)    name doc  ---> name is not defined.


data == programm == stack  / (1) define
=========================

This is the mantra of Scheme and Joy.

A definition in Rabbit-vm is like this:

(add) {a b  --  c  // add two numbers} (+) define

a teaspoon of sugar:
`add {a b --  c  // add two numbers} (+) define

But i would like this:
add: +

Hmm??

[
add: {i i  --  i  // add two numbers} +
sub: -
mul: *
] define-from-list

With this as a start it evolved to:

BEGIN

add {i i --  i  /add two numbers}   +
sub {}   -
mul {} *

END Letf.define



data == programm == stack  / (2) recurse
==========================

From time to time i run into a pattern like this:
fac-2 doesn't work! Because copy+paste didn't change the name fac-1 into fac-2.

fac-1: ( 0 = )( zap 1 )( dup 1 - fac-1 * )if*

fac-2: dup 0 = ( zap 1 )( dup 1 - fac-1 * )if/


In the following 'loop' is just a symbol, that is going to be replaced by the name of the currently defined word. This saves typing and is an improvement on legibility.

fac-1: ( 0 = )( zap 1 )( dup 1 - loop * )if*

fac-2: dup 0 = ( zap 1 )( dup 1 - loop * )if/



data == programm == stack  / (3) tests
=========================

`sample-tests {} BEGIN

*:        3 4 TX                -> 12
+:        3 4 TX   5 6 TX -> 7 11

END define



data == programm == stack  / (4) the pattern
============================

(a) write down, what you would like to write
(b) find a data-structure, you would like to work with
(c) write function foo: (a) -> (b)





stack and program-history or state-transition
===========================

The stack is an arbitrary complex data structur. Well, most of the time it looks like a stack and is unsed like a stack. The property  arbitary-complex-data-structur includes arbitrary simple data structures like:

stack == 1

So, in order to liberate the stack from the burden of complexity and from the burden of state transition it is helpful to have an other data structur for:

state transition (== global varibles)
intermediate results (== local variables)
parameters (== function parameters)

This is what i call upstack, dmbarbour calls it a hand (i'm not sure if i know what he means.)
I think it's just context.



Rabbit-vm
======

W o r l d                |       r a b b i t           -            v m
                             |                                                             
                                                                          hand/upstack/context (??? property-list)
                                                                              |
                                                                              |
(     world of      )        ------ parse  --------->               
(   char strings   )      <---- print   -----------            VM  <--->    definitions (hashtable)
                                                                               |
                                                                               |
                                                                           stack
                                                                       (linked list)


Rabbit-vm does two things:

   *  parse:  string of characters -->  items
   *  execute as Joy does.



So, this is what i can contribute to the discussion.

I think there are lots of running implemetations of pure Joy somewhere in the internet, but the only ones i know about are Joy1 in scheme and C form John Cowan, njoy in OCaml from  Christian Szegedy and Conca from Claude Marinier.

I don't know, is it a coincidence or did it just happen at the same time that Claude and i present the same idea within a few days.


The code is at sourceforge at Rabbit-vm. There is currently also a copy of this mail.

https://sourceforge.net/projects/rabbitvm/

Of course I would be happy about feedback.



Best regards
Heiko


Jon Purdy — 2014-05-05 21:19:51

> I would like to use () instead of [].

Syntax design is hard. The argument for [] is that they’re non-shift
keys (on US keyboards) and they free up parentheses for their more
common meanings of grouping (mathematics) or comments (natural
language). I use {} for code quotations in Kitten because they give
you a syntax more resembling C, and that frees up [] for data
quotations (vectors).

> Factor, with its 224MB-installation seems a lot more than i can chew.

I agree. :)

> () (1 2 3) uncons (swons)dip
> () (1 2 3) uncons swons^

We talked about this briefly on the Concatenative Programming G+
group[1], namely that higher-order functions are something like
adverbs, and seem to work well when they look syntactically like
inflections. You could make the combination of word+symbol invoke
“(word) symbol” and avoid a lot of nesting without making “^” special.

[1]: https://plus.google.com/103392068634106050064/posts/gEkDANFd7qp

> (name)
> `name
>
> This is a small difference. The improvement is not in typing but in reading: `name is most of the time a name, passed to a function.

I read those differently:

(name) = “push a new quotation which calls ‘name’”
`name = “push the word ‘name’ itself”

Of course, they can have the same implementation.

> From time to time i run into a pattern like this:
> fac-2 doesn't work! Because copy+paste didn't change the name fac-1 into fac-2.
>
> fac-1: ( 0 = )( zap 1 )( dup 1 - fac-1 * )if*
>
> fac-2: dup 0 = ( zap 1 )( dup 1 - fac-1 * )if/
>
>
> In the following 'loop' is just a symbol, that is going to be replaced by the name of the currently defined word. This saves typing and is an improvement on legibility.
>
> fac-1: ( 0 = )( zap 1 )( dup 1 - loop * )if*
>
> fac-2: dup 0 = ( zap 1 )( dup 1 - loop * )if/
>

Forth implementations sometimes have a “recurse” word for this
purpose. I prefer “recur” or “loop” because it lets you say cute
things like:

def repl: read evaluate print loop

> I think there are lots of running implemetations of pure Joy somewhere in the internet, but the only ones i know about are Joy1 in scheme and C form John Cowan, njoy in OCaml from Christian Szegedy and Conca from Claude Marinier.

Do you want this to become the standard implementation of Joy? Joy
seems like the concatenative Scheme, in that it’s small and easy to
implement, so there are many incompatible implementations…

> I don't know, is it a coincidence or did it just happen at the same time that Claude and i present the same idea within a few days.

That has a tendency to happen.

> The code is at sourceforge at Rabbit-vm.

You should check it into the repo on SourceForge so that it can be
browsed under “Code” instead of by downloading the tarball. :)

— 2014-05-06 20:33:14

Hi Jon,

thank you for your feedback.


() or []

In my opinion () is a little better to read than [].
((()))   [[[]]]   not much, just a little.

My app translates BEGIN begin [ ( ) ] end END   to   ( ( ( (  ) ) ) )
These four types of braces can be used interchangeable as long
as they match.


>> () (1 2 3) uncons (swons)dip
>> () (1 2 3) uncons swons^

> We talked about this briefly on the Concatenative Programming G+
> group[1], namely that higher-order functions are something like
> adverbs, and seem to work well when they look syntactically like
> inflections.

dip is
the most commonly used word in Joy code i have written so far.
I don't know, if this is because of my way of coding.

> You could make the combination of word+symbol invoke
> “(word) symbol” and avoid a lot of nesting without making “^” special.

I'm not sure about this. Currently i have
`name  ->  (name)
,name  -> (name)first
@name  -> get definition of name
!name -> set definition of name
>name -> push TOS to name      
<name -> pop from name and push to stack
~apply at name

For instance the following programm

`name {} () define
1 >name  2 >name   3 >name   4 >name
(swap)~name
@name

should push (3 4 2 1) to stack.

But i don't use it!
Instead i can't use >name just as name for a word.

I like
,name^^
as
abbreviation for
((name)first)dip2

Currently this sugar is in the parser. Maybe i try to make primitives from it.


> Forth implementations sometimes have a “recurse” word for this
> purpose. I prefer “recur” or “loop” because it lets you say cute
> things like:
>
> def repl: read evaluate print loop

I didn't know this. Thank you.