Alright so I said I was going to start looking at stuff a little bit ago, turns out, that's today.
Anyway, I can do some basic stuff, but I'm pretty confused about how this language works.
- What are the 'var', 'op', and 'stack' thingies by operation definitions in the square brackets. I assumed that they specified the type of the arguments?
- How do I get the length of a stack, or otherwise iterate over it?
- Can I use operations as arguments, and if so, how?
1. These are
not for argument types but tell the operation defined which local scopes should be used. `var` opens a lexical variable environment when the operation is called, `op` a lexical operation environment and `stack` lets the operation use its own stack. So, if you want to define variables that are only visible inside the operation you are defining, you need to put a `VAR` into those square brackets.
2. There is nothing like that in the standard library. I assume you already know what mapping is. Here is an operation or mapping over a stack, using the old operation names:
map [ var ] ;[ `map` uses local variables (the arguments) and thus needs its own lexical variable environment ];
{ op arg~ stack arg~ ;[ remember the implications of postfix notation regarding in which order arguments are taken from the stack – I will try to rectify this with the `[ arg1 arg2 … ]args` construct ];
stack get [] ! == & ;[ check whether the stack is empty ];
[ [] ! ;[ if it is, we just return the empty stack ];
]# ;[ otherwise: ];
[ stack get _ & op get map call ;[ first map over the rest of the stack ];
stack get : & op get call ;[ then call our operation on the top element of the stack ];
<- & ;[ then push the result of that back onto the mapped rest of the stack ];
]#
? ;[ decide which of the two branches should be called ];
}
3. Yes, you can. See below.
I tried doing something like this:
do-op [ ]
{
op arg~
b arg~
a arg~
a get b get op get & &
}
but it just puts the arguments back on the stack, it doesn't actually do the operation.
Always keep in mind that, when an operation is defined, there is already poslin code running that, in a way, assembles the operation you are defining.
Specifically this is about immediate functions.
Now your code has two issues, one unrelated to the problem you are describing. That unrelated problem is that this operation does not use its own variable environment, so it will instead just write your arguments into the variable environment in which you are calling this operation. To avoid that, place a `var` into the square brackets behind te operation name.
The other issue lies in this specific chunk of code:
op get & &
This contains two different immediate functions, `get` and `&`. These are called as soon as they are read.
`get` dumps a thread onto your current stack (which will later be transformed into a thread itself). his thread is responsible for retrieving the value of `op`.
`&` takes an object and transforms it into a thread. That means, in this special case, the first `&` takes the thread `get` dumped and then transforms it into a thread, which is a no-op. The second `&` again takes that thread and then puts it back, because it already is a thread.
What you want is to place a thread here which calls the object on top of the thread. The corresponding operation is `!`. Now, keep in mind that `!` is immediate but you don't want it to be called now but rather you want its thread. You need to quote it so the symbol `!` ends up on your stack and then transform that symbol into a thread using `&`. So the code chunk should look like this:
op get '! &
This is a bit ugly, so there is another immediate operation, which does that for you, called `call`.
op get call ;[ as seen in the implementation of `map` above ];
So, your operation actually should look like this:
do-op [ var ]
{
op arg~
b arg~
a arg~
a get b get op get call
}
If you've got any questions, I am happy to answer them. I need to learn explaining this stuff either way – after all, if I want this to be used, I'll have to write a tutorial.