Using Combinators

A combinator is a function that consumes functions as input. They are used heavily by the RETRO system.

Types of Combinators

Combinators are divided into three primary types: compositional, execution flow, and data flow.

Compositional

A compositional combinator takes elements from the stack and returns a new quote.

curry takes a value and a quote and returns a new quote applying the specified quote to the specified value. As an example,

```
:acc (n-)  here swap , [ dup v:inc fetch ] curry ;
```


This would create an accumulator function, which takes an initial value and returns a quote that will increase the accumulator by 1 each time it is invoked. It will also return the latest value. So:

```
#10 acc
dup call n:put
dup call n:put
dup call n:put
```


Execution Flow

Combinators of this type execute other functions.

Fundamental

call takes a quote and executes it immediately.

```
[ #1 n:put ] call
&words call
```


Conditionals

RETRO provides three primary combinators for use with conditional execution of quotes. These are choose, if, and -if.

choose takes a flag and two quotes from the stack. If the flag is true, the first quote is executed. If false, the second quote is executed.

```
#-1 [ 'true s:put ] [ 'false s:put ] choose
 #0 [ 'true s:put ] [ 'false s:put ] choose
```


if takes a flag and one quote from the stack. If the flag is true, the quote is executed. If false, the quote is discarded.

```
#-1 [ 'true s:put ] if
 #0 [ 'true s:put ] if
```


-if takes a flag and one quote from the stack. If the flag is false, the quote is executed. If true, the quote is discarded.

```
#-1 [ 'false s:put ] -if
 #0 [ 'false s:put ] -if
```


RETRO also provides case and s:case for use when you have multiple values to check against. This is similar to a switch in C.

case takes two numbers and a quote. The initial value is compared to the second one. If they match, the quote is executed. If false, the quote is discarded and the initial value is left on the stack.

Additionally, if the first value was matched, case will exit the calling function, but if false, it returns to the calling function.

s:case works the same way, but for strings instead of simple values.

```
:test (n-)
  #1 [ 'Yes s:put ] case
  #2 [ 'No  s:put ] case
  drop 'No idea s:put ;
```


Looping

Several combinators are available for handling various looping constructs.

while takes a quote from the stack and executes it repeatedly as long as the quote returns a true flag on the stack. This flag must be well formed and equal -1 or 0.

```
#10 [ dup n:put sp n:dec dup 0 -eq? ] while
```


times takes a count and quote from the stack. The quote will be executed the number of times specified. No indexes are pushed to the stack.

```
#1 #10 [ dup n:put sp n:inc ] times drop
```


There is also a indexed-times variation that provides access to the loop index (via I) and parent loop indexes (via J and K).

```
#10 [ I n:put sp ] indexed-times
```


Data Flow

These combinators exist to simplify stack usage in various circumstances.

Preserving

Preserving combinators execute code while preserving portions of the data stack.

dip takes a value and a quote, moves the value off the main stack temporarily, executes the quote, and then restores the value.

```
#10 #20 [ n:inc ] dip
```


Would yield the following on the stack:

```
11 20
```


sip is similar to dip, but leaves a copy of the original value on the stack during execution of the quote. So:

```
#10 [ n:inc ] sip
```


Leaves us with:

```
11 10
```


Cleave

Cleave combinators apply multiple quotations to a single value or set of values.

bi takes a value and two quotes, it then applies each quote to a copy of the value.

```
#100 [ n:inc ] [ n:dec ] bi
```


tri takes a value and three quotes. It then applies each quote to a copy of the value.

```
#100 [ n:inc ] [ n:dec ] [ dup * ] tri
```


Spread

Spread combinators apply multiple quotations to multiple values. The asterisk suffixed to these function names signifies that they are spread combinators.

bi* takes two values and two quotes. It applies the first quote to the first value and the second quote to the second value.

```
#1 #2 [ n:inc ] [ #2 * ] bi*
```


tri* takes three values and three quotes, applying the first quote to the first value, the second quote to the second value, and the third quote to the third value.

```
#1 #2 #3 [ n:inc ] [ #2 * ] [ n:dec ] tri*
```


Apply

Apply combinators apply a single quotation to multiple values. The @ sign suffixed to these function names signifies that they are apply combinators.

bi@ takes two values and a quote. It then applies the quote to each value.

```
#1 #2 [ n:inc ] bi@
```


tri@ takes three values and a quote. It then applies the quote to each value.

```
#1 #2 #3 [ n:inc ] tri@
```


RETRO also provides for-each combinators for various data structures. The exact usage of these varies; consult the Glossary and relevant chapters for more details on these.