I tried some stack-based languages to see how the following codes work:
1 [2 3 4] {add} map
1 [2 3 4] {swap} map
add
pops two items off the stack and pushes one back. swap
pops two items off the stack and pushes two back.
As @Michael Homer said, Joy simply treats the input function as a single-return function, and would treat the top element on the stack as the replacement. The rest of the stack is not affected.
1 [2 3 4] [+] map put .
This prints [3 4 5] 1
. Try it online!
1 [2 3 4] [swap] map put .
This prints [1 1 1] 1
. Try it online!
I don't know Factor, so I'm not sure if I'm doing it right. But the result is really interesting.
USING: math sequences ;
1 { 2 3 4 } [ + ] map
This errors as expected. The error message is Data stack underflow
, followed by some stack trace that I don't understand. Attempt This Online!
USING: kernel sequences ;
1 { 2 3 4 } [ swap ] map
It prints (Attempt This Online!):
--- Data stack:
4
{ 1 2 3 }
The result is unexpected but understandable. It seems that it treats map
's function argument as a function that takes one argument, returns one value, and possibly modifies the stack as a side effect. So here, [ swap ] map
first swaps 2
and 1
, where it returns 1
and leaves 2
on the stack. Then it swaps 3
and 2
, and finally 4
and 3
. So the result is { 1 2 3 }
.
The two examples above doesn't seem to check the stack effect. However, a simple modification to the first example gives a stack effect error:
USING: math sequences ;
1 1 1 { 2 3 4 } [ + ] map
Here is the error message (Attempt This Online!):
The input quotation to “map” doesn't match its expected effect
For more information, evaluate:
"inference-branches" help
Input Expected Got
[ + ] ( ... elt -- ... newelt ) ( x x -- x )
I don't understand why adding two 1
s to the stack makes a difference. Does it mean that Factor checks the stack effect after evaluation instead of before?
Kitten is static-typed. I can't find an online interpreter for it, so I tried it on my computer.
1 [2, 3, 4] { (+) } map
It is rejected by compile-time type checking as expected. Here is the error message on REPL:
<interactive>:1.16-17: I can't match the type 'R...'
<interactive>:1.16-17: with the type 'R..., A'
<interactive>:1.16-17: the type 'R...'
<interactive>:1.16-17: occurs in the type 'R..., A' (which often indicates an infinite type)
<interactive>:1.16-17: you may have a stack depth mismatch
However, the second example is accepted:
1 [2, 3, 4] { swap } map
The REPL prints:
[1i32, 2i32, 3i32]
4i32
The result is the same as Factor's. It seems that map
's type isn't as restrictive as I thought. At least it allows functions that push and pop the same number of items, even if this number is not 1.
I read the source code of Kitten's standard library, and found that map
's type signature is map<A, B, +P> (List<A>, (A -> B +P) -> List<B> +P)
. Kitten has a simple effect system called Permissions. +P
might be related to it.
It seems that Attempt This Online! isn't working for Cognate, so I tried it on my computer.
Unlike most other stack-based languages, Cognate is prefix instead of postfix.
Print Map (+) List (2 3 4) 1;
This errors with Stack underflow
.
Print Map (Swap) List (2 3 4) 1;
This also errors with Stack underflow
.
Cognate's documentation isn't very clear, so I don't really know how it works. It might be one example of "error out, because you’ve gone off the end of the isolated stack" in Michael Homer's answer.
Uiua is a new language that combines stack-based and array programming. It is also prefix.
map
is called each
in Uiua, and is represented by the symbol ∵
. Usually we don't need it, because most functions are already vectorized.
∵+ [2 3 4] 1
It prints [3 4 5]
as expected, and shows a warning that ∵
is not needed. Try it online!
∵∶ [2 3 4] 1
swap
is called flip
and is represented by :
. The code above prints(Try it online!):
[1 1 1]
[2 3 4]
The top of the stack is still [2 3 4]
, while the 1
below it becomes [1 1 1]
. This is unexpected. I don't understand it at all.