I tried some stack-based or concatenative languages to see how the following codes work:
1 [2 3 4] {add} map
1 [2 3 4] {swap} map
1 [2 3 4] {dup} map
add
pops two items off the stack and pushes one back. swap
pops two items off the stack and pushes two back. dup
pops one item off the stack and pushes two back.
Joy
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!
1 [2 3 4] [dup] map put .
This prints [2 3 4] 1
. Try it online!
Factor
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 }
.
USING: kernel sequences ;
1 { 2 3 4 } [ dup ] map
It gives a stack effect error (Attempt This Online!):
The input quotation to “map” doesn't match its expected effect
For more information, evaluate:
"inference-branches" help
Input Expected Got
[ dup ] ( ... elt -- ... newelt ) ( x -- x x )
The first 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
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
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.
1 [2, 3, 4] { dup } map
This is also rejected by compile-time type checking.
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.
Cognate
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
.
Print Map (Twin) List (2 3 4) 1;
This prints (2, 2, 3, 3, 4, 4)
. It also shows a warning: Exiting with 1 object(s) on the stack
.
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
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.
∵. [2 3 4] 1
duplicate
is represented by .
. The code above prints(Try it online!):
1
[2 3 4]
[2 3 4]