<p>In many languages, the <code>map</code> function takes a function and a list, and applies the function to each element of the list, returning a new list of the results. Some languages require the input function to be unary, while others allow it to be n-ary and take multiple lists as input.</p>
<p>However, in stack-based languages, functions work on stacks. A function may pop some number of items off the stack, do something with them, and push some number of items back onto the stack.</p>
<p>How does the <code>map</code> function work in stack-based languages? How does it work when the input functions push more than one item onto the stack?</p>
<p>For example, what would the following code (in a hypothetical stack-based language) do?</p>
<pre><code>1 [2 3 4] {swap} map
</code></pre>
<p>One idea I had was to introduce a simple type system, where each function has a fixed arity, and the <code>map</code> function only accepts functions that pop one item off the stack and push one item back. So the above code would be rejected. However, this seems to be too restrictive.</p>
<p>How do real stack-based languages handle this problem?</p>

<functional-programming><stack-based-languages>
How does the map function work in stack-based languages?
CC BY-SA 4.0
2023-11-13T23:27:51.450Z
Many golfing languages just invent different high-order functions for functions with different arity. And the problem is there are many of them, mapping each to each, mapping one to each, and just reduce, without a most sensical default. If you want it not too restrictive, it might be better to start at binary operators and extend it to ternary, thinking unary a degenerated case.
user23013
<p>I tried some stack-based or concatenative languages to see how the following codes work:</p>
<pre><code>1 [2 3 4] {add} map

1 [2 3 4] {swap} map

1 [2 3 4] {dup} map
</code></pre>
<p><code>add</code> pops two items off the stack and pushes one back. <code>swap</code> pops two items off the stack and pushes two back. <code>dup</code> pops one item off the stack and pushes two back.</p>
<h2><a href="https://hypercubed.github.io/joy/joy.html" rel="nofollow noreferrer">Joy</a></h2>
<p>As <a href="https://langdev.stackexchange.com/a/3154/194">@Michael Homer said</a>, 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.</p>
<pre><code>1 [2 3 4] [+] map put .
</code></pre>
<p>This prints <code>[3 4 5] 1</code>. <a href="https://tio.run/##y8qv/P/fUCHaSMFYwSRWIVo7ViE3sUChoLREQY/r/38A" rel="nofollow noreferrer">Try it online!</a></p>
<pre><code>1 [2 3 4] [swap] map put .
</code></pre>
<p>This prints <code>[1 1 1] 1</code>. <a href="https://tio.run/##y8qv/P/fUCHaSMFYwSRWIbq4PLEgViE3sUChoLREQY/r/38A" rel="nofollow noreferrer">Try it online!</a></p>
<pre><code>1 [2 3 4] [dup] map put .
</code></pre>
<p>This prints <code>[2 3 4] 1</code>. <a href="https://tio.run/##y8qv/P/fUCHaSMFYwSRWITqltCBWITexQKGgtERBj@v/fwA" rel="nofollow noreferrer">Try it online!</a></p>
<h2><a href="https://factorcode.org/" rel="nofollow noreferrer">Factor</a></h2>
<p>I don't know Factor, so I'm not sure if I'm doing it right. But the result is really interesting.</p>
<pre><code>USING: math sequences ;

1 { 2 3 4 } [ + ] map
</code></pre>
<p>This errors as expected. The error message is <code>Data stack underflow</code>, followed by some stack trace that I don't understand. <a href="https://ato.pxeger.com/run?1=m70sLTG5JL9owYKlpSVpuhY39UKDPf3crRRyE0syFIpTC0tT85JTixWsubgMFaoVjBSMFUwUahWiFbQVYoFqCiC6oJphhgAA" rel="nofollow noreferrer">Attempt This Online!</a></p>
<pre><code>USING: kernel sequences ;

1 { 2 3 4 } [ swap ] map
</code></pre>
<p>It prints (<a href="https://ato.pxeger.com/run?1=m70sLTG5JL9owYKlpSVpuhY3jUODPf3crRSyU4vyUnMUilMLS1PzklOLFay5uAwVqhWMFIwVTBRqFaIVissTCxRiFXITCyBaoSbATAIA" rel="nofollow noreferrer">Attempt This Online!</a>):</p>
<pre><code>--- Data stack:
4
{ 1 2 3 }
</code></pre>
<p>The result is unexpected but understandable. It seems that it treats <code>map</code>'s function argument as a function that takes one argument, returns one value, and possibly modifies the stack as a side effect. So here, <code>[ swap ] map</code> first swaps <code>2</code> and <code>1</code>, where it returns <code>1</code> and leaves <code>2</code> on the stack. Then it swaps <code>3</code> and <code>2</code>, and finally <code>4</code> and <code>3</code>. So the result is <code>{ 1 2 3 }</code>.</p>
<pre><code>USING: kernel sequences ;

1 { 2 3 4 } [ dup ] map
</code></pre>
<p>It gives a stack effect error (<a href="https://ato.pxeger.com/run?1=m70sLTG5JL9owYKlpSVpuhY3jUKDPf3crRSyU4vyUnMUilMLS1PzklOLFay5uAwVqhWMFIwVTBRqFaIVUkoLFGIVchMLIDqhBsAMAgA" rel="nofollow noreferrer">Attempt This Online!</a>):</p>
<pre><code>The input quotation to “map” doesn't match its expected effect

For more information, evaluate:
    &quot;inference-branches&quot; help

Input   Expected                  Got
[ dup ] ( ... elt -- ... newelt ) ( x -- x x )
</code></pre>
<p>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:</p>
<pre><code>USING: math sequences ;

1 1 1 { 2 3 4 } [ + ] map
</code></pre>
<p><a href="https://ato.pxeger.com/run?1=m70sLTG5JL9owYKlpSVpuhY3jUKDPf3crRRyE0syFIpTC0tT85JTixWsubgMFUCwWsFIwVjBRKFWIVpBWyEWqK4AohNqAMwgAA" rel="nofollow noreferrer">Attempt This Online!</a></p>
<p>I don't understand why adding two <code>1</code>s to the stack makes a difference. Does it mean that Factor checks the stack effect after evaluation instead of before?</p>
<h2><a href="https://kittenlang.org/" rel="nofollow noreferrer">Kitten</a></h2>
<p>Kitten is static-typed. I can't find an online interpreter for it, so I tried it on my computer.</p>
<pre><code>1 [2, 3, 4] { (+) } map
</code></pre>
<p>It is rejected by compile-time type checking as expected. Here is the error message on REPL:</p>
<pre><code>&lt;interactive&gt;:1.16-17: I can't match the type 'R...'
&lt;interactive&gt;:1.16-17: with the type 'R..., A'

&lt;interactive&gt;:1.16-17: the type 'R...'
&lt;interactive&gt;:1.16-17: occurs in the type 'R..., A' (which often indicates an infinite type)

&lt;interactive&gt;:1.16-17: you may have a stack depth mismatch
</code></pre>
<p>However, the second example is accepted:</p>
<pre><code>1 [2, 3, 4] { swap } map
</code></pre>
<p>The REPL prints:</p>
<pre><code>[1i32, 2i32, 3i32]
4i32
</code></pre>
<p>The result is the same as Factor's. It seems that <code>map</code>'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.</p>
<pre><code>1 [2, 3, 4] { dup } map
</code></pre>
<p>This is also rejected by compile-time type checking.</p>
<p>I read <a href="https://github.com/evincarofautumn/kitten/blob/2bbc264d7f05c4a7d7b35d06773d1ab2f0623193/common.ktn#L1341" rel="nofollow noreferrer">the source code of Kitten's standard library</a>, and found that <code>map</code>'s type signature is <code>map&lt;A, B, +P&gt; (List&lt;A&gt;, (A -&gt; B +P) -&gt; List&lt;B&gt; +P)</code>. Kitten has a simple effect system called <a href="https://kittenlang.org/intro/#permissions" rel="nofollow noreferrer">Permissions</a>. <code>+P</code> might be related to it.</p>
<h2><a href="https://cognate-lang.github.io/" rel="nofollow noreferrer">Cognate</a></h2>
<p>It seems that <a href="https://ato.pxeger.com/" rel="nofollow noreferrer">Attempt This Online!</a> isn't working for Cognate, so I tried it on my computer.</p>
<p>Unlike most other stack-based languages, Cognate is prefix instead of postfix.</p>
<pre><code>Print Map (+) List (2 3 4) 1;
</code></pre>
<p>This errors with <code>Stack underflow</code>.</p>
<pre><code>Print Map (Swap) List (2 3 4) 1;
</code></pre>
<p>This also errors with <code>Stack underflow</code>.</p>
<pre><code>Print Map (Twin) List (2 3 4) 1;
</code></pre>
<p>This prints <code>(2, 2, 3, 3, 4, 4)</code>. It also shows a warning: <code>Exiting with 1 object(s) on the stack</code>.</p>
<p>Cognate's documentation isn't very clear, so I don't really know how it works. It might be one example of &quot;error out, because you’ve gone off the end of the isolated stack&quot; in Michael Homer's answer.</p>
<h2><a href="https://www.uiua.org/" rel="nofollow noreferrer">Uiua</a></h2>
<p>Uiua is a new language that combines stack-based and array programming. It is also prefix.</p>
<p><code>map</code> is called <a href="https://www.uiua.org/docs/each" rel="nofollow noreferrer"><code>each</code></a> in Uiua, and is represented by the symbol <code>∵</code>. Usually we don't need it, because most functions are already vectorized.</p>
<pre><code>∵+ [2 3 4] 1
</code></pre>
<p>It prints <code>[3 4 5]</code> as expected, and shows a warning that <code>∵</code> is not needed. <a href="https://www.uiua.org/pad?src=0_2_0__4oi1KyBbMiAzIDRdIDEK" rel="nofollow noreferrer">Try it online!</a></p>
<pre><code>∵∶ [2 3 4] 1
</code></pre>
<p><code>swap</code> is called <a href="https://www.uiua.org/docs/flip" rel="nofollow noreferrer"><code>flip</code></a> and is represented by <code>:</code>. The code above prints (<a href="https://www.uiua.org/pad?src=0_2_0__4oi14oi2IFsyIDMgNF0gMQo=" rel="nofollow noreferrer">Try it online!</a>):</p>
<pre><code>[1 1 1]
[2 3 4]
</code></pre>
<p>The top of the stack is still <code>[2 3 4]</code>, while the <code>1</code> below it becomes <code>[1 1 1]</code>. This is unexpected. I don't understand it at all.</p>
<pre><code>∵. [2 3 4] 1
</code></pre>
<p><a href="https://www.uiua.org/docs/duplicate" rel="nofollow noreferrer"><code>duplicate</code></a> is represented by <code>.</code>. The code above prints (<a href="https://www.uiua.org/pad?src=0_2_0__4oi1LiBbMiAzIDRdIDEK" rel="nofollow noreferrer">Try it online!</a>):</p>
<pre><code>1
[2 3 4]
[2 3 4]
</code></pre>

CC BY-SA 4.0
2023-11-14T08:48:37.283Z
2023-11-14T09:14:32.080Z
2023-11-14T09:14:32.080Z
alephalpha
CC BY-SA 4.0
2023-11-14T08:53:46.537Z
For Kitten, I'm not entirely sure what's supposed to work - if you run `"x" [1,2,3] {swap} map` you get a heterogeneous list and no error, but lists aren't meant to be mixed-type and `//type "x" [1,2,3] {swap} map` tells you that doesn't happen. Jon is on the site, so maybe we'll find out if that's a bug.
Michael Homer
CC BY-SA 4.0
2023-11-14T09:05:39.183Z
For Uiua, it seems that `∵+ [2 3 4] 1` is copying the whole argument list in (so the 1 gets reused like it did for `+`), then taking each separate output of the function as contributing to a separate output array, like in `∵⊃+× [2 3 4 5] 10` (which produces two arrays, `[12 13 14 15]` and `[20 30 40 50]`). Swap has one output that's a 1 and one that's the original value.
Michael Homer
CC BY-SA 4.0
2023-11-19T15:24:30.210Z
"`{ 1 2 3 }` ... The result is unexpected but understandable." – My guess would have been that `1 { 2 3 4 } [ swap ] map` would essentially do "push 1, push 2, swap, pop, push 3, swap, pop, push 4, swap, pop, push a list of the three things we popped." It looks like that's exactly what it did. I'm curious what you were expecting.
Sophie Swett
CC BY-SA 4.0
2023-11-20T20:40:04.823Z
@MichaelHomer: Yep, that’s a bug. Ignoring permissions, and making the stack explicit, the signature of `map` in conventional notation is ∀ *sab*. *s* × List *a* × (∀ *t*. *t* × *a* → *t* × *b*) → *s* × List *b*. Because *a* and *b* both happen to be Int32, the typechecker rightly goes on to solve *t* = *u* × Int32 for a fresh metavariable *u*. However, it’s wrong to accept that. The problem is that *t* is instantiated as a metavar, which can unify with other things—in this case, because of the quantifier, it should be a Skolem constant, which can’t. The interpreter result after that is bogus.
Jon Purdy
<p>The typed approach you describe generally <em>is</em> how typed stack-based languages work, sometimes with a little more flexibility when you have the stack before and after line up. You won't be permitted to push more than one output value, or to have <em>no</em> output.</p>
<p>For example, in <a href="https://kittenlang.org/" rel="nofollow noreferrer">Kitten</a>, the <a href="https://kittenlang.org/intro/#polymorphism" rel="nofollow noreferrer">type of <code>map</code></a> is <code>&lt;A, B&gt; (List&lt;A&gt;, (A -&gt; B) -&gt; List&lt;B&gt;)</code> - taking in a list of A and a function from A to B. For a fixed function, this isn’t any more restrictive than the corresponding signature from Haskell (<code>(a -&gt; b) -&gt; [a] -&gt; [b]</code>).</p>
<p>Factor isn’t typed to the same extent, but its stack effects do <a href="https://docs.factorcode.org/content/word-map%2Csequences.html" rel="nofollow noreferrer">specify the same shape for <code>map</code>’s function argument</a>: <code>( ... elt -- ... newelt )</code>; this means that the stack has to be the same shape before and after applying the quotation, but the data types don't have to match.</p>
<p>For a language with quotation combinators to inject arguments, there's typically no loss of generality from requiring the function be typed this way. For a language that mandates you leave the rest of the stack in the state you found it, you might be able to use some values non-destructively, under the same conditions as for untyped languages below.</p>
<p>For a fully-typed system something like <code>{swap} map</code> is necessarily prohibited, because the type of the resulting elements from lower on the stack is unknown, so the resulting collection would be heterogeneous. For something like Factor where the goal is only stack safety, this can be ok.</p>
<p>In both cases, something like <code>{dup} map</code> is a no go: it's a static error that the stack depth mismatches. Pushing more than one value to the stack is not allowed, because there would be unknown numbers of values left afterwards, so it simply doesn't work.</p>
<hr />
<p>Untyped languages are a slightly different story, since they don't have to care about keeping the stack values in a known state. The canonical stack-based language, Forth, <em>in general</em> just doesn’t have these sorts of higher-order functions in the first place — it’s aimed at a more machine-level execution model. Others that do, like Joy and some higher-level Forth dialects, most often treat the operation as a single-return function, and would treat the top element on the stack as the replacement. Depending on their implementation, something like <code>1 [ 2 3 4 ] { swap } map</code> might</p>
<ul>
<li>Work as you probably expected (-1 to every item); this is <a href="https://hypercubed.github.io/joy/html/j03atm.html" rel="nofollow noreferrer">what would happen in Joy</a>.</li>
<li>Error out, because you’ve gone off the end of the isolated stack containing only arguments that this language evaluates functions on, and lower items were never moved in.</li>
<li>Just break, because you’ve interfered with the internal state of the <code>map</code> function, which is what was on the stack immediately below the element you’re processing, and when it resumes it does something wrong or illegal.</li>
</ul>
<p>Any of those is generally reasonable, though the latter is probably the more principled stack-based approach and what you’d expect in a machine-level Forth, for example. The first behaviour can be obtained either by “cheating” with state off the language-level data stack, or by including a separate parallel stack for iteration data (<a href="https://blog.information-superhighway.net/a-forth-vocabulary-for-iteration" rel="nofollow noreferrer">here</a>’s one that does that). The middle case is either a leaky implementation strategy, or really an instance of being a little bit typed, like Factor.</p>
<p>If you <code>dup</code> instead of <code>swap</code>, you'd still break in the final case, probably leave copies of all the items behind in the first case, and who knows in the middle case. It’s conceivable that a system could take <em>all</em> of the added stack items and put them all into the output collection (so really performing a flat map/bind rather than a functor map), but I couldn’t name one that does.</p>
<p>For the parallel/zip-map case you mention, Joy has a two-list map operation, but no higher than that. It just pushes and takes the two items in order. You could swap items between lists if you wanted.</p>

CC BY-SA 4.0
2023-11-12T08:11:59.890Z
2023-11-14T08:39:51.297Z
2023-11-14T08:39:51.297Z
Michael Homer
CC BY-SA 4.0
2023-11-14T03:21:19.287Z
I tried `1 { 2 3 4 } [ swap ] map` on Factor. The result is really interesting: [Attempt This Online!](https://ato.pxeger.com/run?1=m70sLTG5JL9owYKlpSVpuhY3jUODPf3crRSyU4vyUnMUilMLS1PzklOLFay5uAwVqhWMFIwVTBRqFaIVissTCxRiFXITCyBaoSbATAIA) It seems that it first swaps `2` and `1`, and then `3` and `2`, and finally `4` and `3`. The result is `{ 1 2 3 }`.
alephalpha
CC BY-SA 4.0
2023-11-14T08:42:18.833Z
Right, `[swap] map` actually *is* OK from Factor's perspective because it leaves the stack shape the same; something more substantive (`dup`, or anything at all pushing multiple values, or no value) wouldn't be. I shouldn't have conflated them together at the top.  I have revised the answer so it sets out the cases clearly.
Michael Homer
# How does the map function work in stack-based languages?
Posted by: username_0 on 2023-11-12
In many languages, the map function takes a function and a list, and applies the function to each element of the list, returning a new list of the results. Some languages require the input function to be unary, while others allow it to be n-ary and take multiple lists as input.
However, in stack-based languages, functions work on stacks. A function may pop some number of items off the stack, do something with them, and push some number of items back onto the stack.
How does the map function work in stack-based languages? How does it work when the input functions push more than one item onto the stack?
For example, what would the following code (in a hypothetical stack-based language) do?
1 [2 3 4] {swap} map

One idea I had was to introduce a simple type system, where each function has a fixed arity, and the map function only accepts functions that pop one item off the stack and push one item back. So the above code would be rejected. However, this seems to be too restrictive.
How do real stack-based languages handle this problem?
**Tags:** functional-programming stack-based-languages
> **Comments:**
> **username_1**: Many golfing languages just invent different high-order functions for functions with different arity. And the problem is there are many of them, mapping each to each, mapping one to each, and just reduce, without a most sensical default. If you want it not too restrictive, it might be better to start at binary operators and extend it to ternary, thinking unary a degenerated case.  
---
## 2 answers
### Answer by username_2 on 2023-11-12. Score: 8 | Accepted answer
The typed approach you describe generally *is* how typed stack-based languages work, sometimes with a little more flexibility when you have the stack before and after line up. You won't be permitted to push more than one output value, or to have *no* output.

For example, in [Kitten](https://kittenlang.org/), the [type of `map`](https://kittenlang.org/intro/#polymorphism) is  `(List, (A -> B) -> List)` - taking in a list of A and a function from A to B. For a fixed function, this isn’t any more restrictive than the corresponding signature from Haskell (`(a -> b) -> [a] -> [b]`).

Factor isn’t typed to the same extent, but its stack effects do [specify the same shape for `map`’s function argument](https://docs.factorcode.org/content/word-map%2Csequences.html): `( ... elt -- ... newelt )`; this means that the stack has to be the same shape before and after applying the quotation, but the data types don't have to match.

For a language with quotation combinators to inject arguments, there's typically no loss of generality from requiring the function be typed this way. For a language that mandates you leave the rest of the stack in the state you found it, you might be able to use some values non-destructively, under the same conditions as for untyped languages below.

For a fully-typed system something like `{swap} map` is necessarily prohibited, because the type of the resulting elements from lower on the stack is unknown, so the resulting collection would be heterogeneous. For something like Factor where the goal is only stack safety, this can be ok.

In both cases, something like `{dup} map` is a no go: it's a static error that the stack depth mismatches. Pushing more than one value to the stack is not allowed, because there would be unknown numbers of values left afterwards, so it simply doesn't work.

---

Untyped languages are a slightly different story, since they don't have to care about keeping the stack values in a known state. The canonical stack-based language, Forth, *in general* just doesn’t have these sorts of higher-order functions in the first place — it’s aimed at a more machine-level execution model. Others that do, like Joy and some higher-level Forth dialects, most often treat the operation as a single-return function, and would treat the top element on the stack as the replacement. Depending on their implementation, something like `1 [ 2 3 4 ] { swap } map` might

* Work as you probably expected (-1 to every item); this is [what would happen in Joy](https://hypercubed.github.io/joy/html/j03atm.html).
* Error out, because you’ve gone off the end of the isolated stack containing only arguments that this language evaluates functions on, and lower items were never moved in.
* Just break, because you’ve interfered with the internal state of the `map` function, which is what was on the stack immediately below the element you’re processing, and when it resumes it does something wrong or illegal.

Any of those is generally reasonable, though the latter is probably the more principled stack-based approach and what you’d expect in a machine-level Forth, for example. The first behaviour can be obtained either by “cheating” with state off the language-level data stack, or by including a separate parallel stack for iteration data ([here](https://blog.information-superhighway.net/a-forth-vocabulary-for-iteration)’s one that does that). The middle case is either a leaky implementation strategy, or really an instance of being a little bit typed, like Factor.

If you `dup` instead of `swap`, you'd still break in the final case, probably leave copies of all the items behind in the first case, and who knows in the middle case. It’s conceivable that a system could take *all* of the added stack items and put them all into the output collection (so really performing a flat map/bind rather than a functor map), but I couldn’t name one that does.

For the parallel/zip-map case you mention, Joy has a two-list map operation, but no higher than that. It just pushes and takes the two items in order. You could swap items between lists if you wanted.
> **Comments:**
> **username_0** (2023-11-14): I tried `1 { 2 3 4 } [ swap ] map` on Factor. The result is really interesting: [Attempt This Online!](https://ato.pxeger.com/run?1=m70sLTG5JL9owYKlpSVpuhY3jUODPf3crRSyU4vyUnMUilMLS1PzklOLFay5uAwVqhWMFIwVTBRqFaIVissTCxRiFXITCyBaoSbATAIA) It seems that it first swaps `2` and `1`, and then `3` and `2`, and finally `4` and `3`. The result is `{ 1 2 3 }`.  
> **username_2** (2023-11-14): Right, `[swap] map` actually *is* OK from Factor's perspective because it leaves the stack shape the same; something more substantive (`dup`, or anything at all pushing multiple values, or no value) wouldn't be. I shouldn't have conflated them together at the top.  I have revised the answer so it sets out the cases clearly.  
---
### Answer by username_0 on 2023-11-14. Score: 5
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

Attempt This Online!
I don't understand why adding two 1s 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:
:1.16-17: I can't match the type 'R...'
:1.16-17: with the type 'R..., A'

:1.16-17: the type 'R...'
:1.16-17: occurs in the type 'R..., A' (which often indicates an infinite type)

: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 (List, (A -> B +P) -> List +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]
> **Comments:**
> **username_2** (2023-11-14): For Kitten, I'm not entirely sure what's supposed to work - if you run `"x" [1,2,3] {swap} map` you get a heterogeneous list and no error, but lists aren't meant to be mixed-type and `//type "x" [1,2,3] {swap} map` tells you that doesn't happen. Jon is on the site, so maybe we'll find out if that's a bug.  
> **username_2** (2023-11-14): For Uiua, it seems that `∵+ [2 3 4] 1` is copying the whole argument list in (so the 1 gets reused like it did for `+`), then taking each separate output of the function as contributing to a separate output array, like in `∵⊃+× [2 3 4 5] 10` (which produces two arrays, `[12 13 14 15]` and `[20 30 40 50]`). Swap has one output that's a 1 and one that's the original value.  
> **username_3** (2023-11-19): "`{ 1 2 3 }` ... The result is unexpected but understandable." – My guess would have been that `1 { 2 3 4 } [ swap ] map` would essentially do "push 1, push 2, swap, pop, push 3, swap, pop, push 4, swap, pop, push a list of the three things we popped." It looks like that's exactly what it did. I'm curious what you were expecting.  
> **username_4** (2023-11-20): @MichaelHomer: Yep, that’s a bug. Ignoring permissions, and making the stack explicit, the signature of `map` in conventional notation is ∀ *sab*. *s* × List *a* × (∀ *t*. *t* × *a* → *t* × *b*) → *s* × List *b*. Because *a* and *b* both happen to be Int32, the typechecker rightly goes on to solve *t* = *u* × Int32 for a fresh metavariable *u*. However, it’s wrong to accept that. The problem is that *t* is instantiated as a metavar, which can unify with other things—in this case, because of the quantifier, it should be a Skolem constant, which can’t. The interpreter result after that is bogus.  
---
