Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The Compose Language

Compose is a concatenative, stack-based programming language. Programs in Compose are built by writing sequenced functions that modify the stack. Compose is functional programming inspired and makes use of functions as data. In concatenative languages, the flow of data is entirely determined by the stack. Each function operates on the stack: it takes some number of values from the top, performs a computation, and pushes results back onto the stack. Calling two functions side by side means “apply the first function, then the second, passing along the stack as the argument.”

For example, in a simple concatenative style:

2 3 +

This pushes 2 and 3 onto the stack, then applies the + function, adding the two numbers and leaving 5 on the stack.

Compose follows this model with a minimal set of primitives, allowing programs to be constructed by composing small, reusable functions. Its design emphasizes consistent stack behavior and predictable composition, keeping the core language simple and easily comprehensible.

Features

Lambda Composition

Compose allows functions to be stored as data on the stack as lambdas, and these lambdas can be composed into more complex functions. They can be manipulated like other stack data, and passed around as arguments for other functions.

Lambda Composition Example

List Processing

Compose provides the map, filter, and fold functions for processing lists. Lists can store an unlimited number of items, and do not require that their elements be the same type.

List Processing Example

Recursive Functions

Compose allows the definition of recursive functions. Currently it only supports recursion of named functions, but recursion from within lambdas is a planned feature.

Recursive Functions Example

Installation

The Compose interactive environment, Compositor, can be installed easily with Cargo.

cargo install compositor

To start the interactive environment, use command cmpstr, and to exit from within the environment, use !exit.

Data

Data terms are functions that push the data item they represent to the stack.

Integers

Integers are dynamic-size, signed whole numbers.

0, 100, -2000, etc.

Booleans

Booleans are true or false values.

true, false

Lambdas

Lambdas are anonymous functions that can be stored and manipulated like data.

( copy * ), ( 1 + ), ( 2 % 0 = ), etc.

Lists

Lists are collections of unlimited length. They can store items of different types.

[ 1 2 3 4 5 ], [ true false false true ], [ 1 true 2 false 3 true ], etc.

Core Functions

Core functions are named functions built into the Compose language.

copy

Copies the item on top of the stack.

true copytrue true

1 2 copy1 2 2

[ 1 2 3 4 5 ] copy[ 1 2 3 4 5 ] [ 1 2 3 4 5 ]

hop

Copies the item on top of the stack.

false true hopfalse true false

10 20 hop hop10 20 10 20

[ 1 2 3 4 5 ] copy[ 1 2 3 4 5 ] [ 1 2 3 4 5 ]

pick

Copies the item at a specified index on the stack. Indexes wrap around the stack, and negative indexes start at the bottom of the stack.

1 2 3 4 5 2 pick1 2 3 4 5 3

false true 3 pickfalse true false

1 2 3 4 5 -1 pick1 2 3 4 5 1

drop

Removes an item from the top of the stack.

true false droptrue

1 2 3 4 5 drop drop1 2 3

[ 1 2 3 4 5 ] [ 6 7 8 9 10 ] drop[ 1 2 3 4 5 ]

swap

Swaps the two items on top of the stack.

10 20 swap20 10

false true swap swapfalse true

rotate

Moves the item on top of the stack under the two items below it.

1 2 3 rotate3 1 2

10 20 30 rotate rotate rotate10 20 30

+

Computes the sum of two integers on top of the stack.

5 10 +15

-500 200 +-300

-

Computes the difference of two integers on top of the stack.

20 10 -10

50 100 --50

*

Computes the product of two integers on top of the stack.

10 20 *200

-5 6 *-30

/

Computes the quotient of two integers on top of the stack.

-100 20 /-5

20 6 /3

%

Computes the remainder of two integers on top of the stack.

-100 9 %-1

20 6 %2

+

Computes the Boolean NOT value for a boolean value on top of the stack.

true !false

false ! !false

|

Computes the Boolean OR value for two boolean values on top of the stack.

false false |false

false true |true

true true |true

&

Computes the Boolean AND value for two boolean values on top of the stack.

false false &false

false true &false

true true &true

^

Computes the Boolean XOR value for two boolean values on top of the stack.

false false ^false

false true ^true

true true ^false

=

Consumes the top two items on the stack and produces true if they are equal, and false if they are not.

false false =true

5 10 =false

0 0 =true

100 true =false

>

Consumes two integers from the top of the stack and produces true if the integer second from the top is greater than the integer on top, otherwise produces false.

5 0 >true

-5 0 >false

0 0 >false

<

Consumes two integers from the top of the stack and produces true if the integer second from the top is less than the integer on top, otherwise produces false.

5 0 <false

-5 0 <true

0 0 <false

apply

Consumes a lambda and applies it to the top of the stack.

5 ( 2 * ) apply10

6 ( copy * ) apply36

100 100 ( hop hop = ) apply100 100 true

under

Consumes a lambda and applies it to the stack under the top item of the stack.

4 5 ( 2 * ) under8 5

5 6 ( copy * ) under25 6

deep

Consumes an integer index and a lambda below it, and applies the lambda at that index in the stack. Indexes wrap around the stack, and negative indexes start at the bottom of the stack.

1 2 3 4 5 ( copy * ) 2 deep1 2 9 4 5

true false false ( copy copy ) -1 deeptrue true true false false

1 2 3 4 5 ( drop drop ) 8 deep3 4 5

compose

Composes two lambdas on top of the stack into a single lambda.

( 2 * ) ( 2 + ) compose( 2 * 2 + )

( 2 % ) ( 1 = ) compose( 2 % 1 = )

?

Consumes two lambdas from the top of the stack, and a boolean below them. If the boolean is true, the bottom lambda is applied to the stack, and if it is false, the top lambda is applied to the stack.

5 false ( 2 * ) ( 2 + ) ?7

5 true ( 2 * ) ( 2 + ) ?10

append

Consumes an item from the top of the stack and appends it to the list below it.

[ 2 4 6 ] 8 append[ 2 4 6 8 ]

[] 1 append 2 append 3 append[ 1 2 3 ]

[] [] append[ [ ] ]

index

Consumes a list and returns the item at a specified index. Indexes wrap around the list, and negative indexes start at the end of the list.

[ 1 2 3 4 5 ] copy 2 index[ 1 2 3 4 5 ] 3

[ 1 2 3 4 5 ] 5 index1

[ 1 2 3 4 5 ] -1 index5

join

Joins two lists on top of the stack.

[ 1 2 3 ] [ 4 5 6 ] join[ 1 2 3 4 5 6 ]

[ true ] [ false ] join[ true false ]

length

Consumes a list from the top of the stack and returns its length.

[ 1 2 3 4 5 ] length5

[ 1 2 3 4 5 ] copy length[ 1 2 3 4 5 ] 5

[] length0

map

Consumes a lambda, and a list below it, and produces the list with each item in it mapped using the lambda. The lambda must consume the original list item and produce a new one.

[ 1 2 3 4 5 ] ( 2 * ) map[ 2 4 6 8 10 ]

[ true false true ] ( ! ) map[ false true false ]

filter

Consumes a lambda, and a list below it, and produces a list containing only items from the original list for which the lambda produces true. The lambda must consume the original list item and produce a boolean.

[ 1 2 3 4 5 6 7 8 9 10 ] ( 2 % 0 = ) filter[ 2 4 6 8 10 ]

[ true false true ] ( true = ) filter[ true true ]

fold

Consumes a lambda, a list, and an accumulator value. Reduces the list from left to right by applying the lambda to the accumulator and each list item. The lambda must consume the accumulator and list item, and produce a new accumulator. The final accumulator value is produced on top of the stack.

0 [ 1 2 3 4 ] ( + ) fold10

1 [ 1 2 3 4 ] ( * ) fold24