Polynomix Quick Start

Polynomix is a language that mixes array-based, procedural, and functional programming into one peculiar package. Sounds intimidating? Don't worry—you can safely ignore all the buzzwords for now and just start writing code.

Hello, World

`Hello, world' >.

Run this, and the console prints Hello, world!. That's it. You've written your first Polynomix program. Everything after this is just details.

Well... important details. But still.

Basic Operations

Before we do anything fancy, let's use Polynomix as a glorified calculator:

1 + 2

The result is 3. Shocker.

Besides the usual + - * / %, Polynomix also gives you greatest common divisor & and least common multiple |. Because sometimes you just need to factor some numbers, okay?

Note: - is not a negative sign. To write a negative number, use a backslash: \10 means -10. This will feel weird at first. You'll get used to it.

Note: No decimals. But you do get arbitrary-precision fractions! So 1.5 is written as 3/2. Your inner mathematician will appreciate this.

Note: Expressions are evaluated strictly left to right. There is no operator precedence. 2 + 3 * 4 is 20, not 14, because the interpreter sees it as (2 + 3) * 4.

If this upsets you, use parentheses:

2 + (3 * 4)

Now it's 14. Order has been restored.

Start Programming

Variables

Use # to assign values:

10 # a

This assigns 10 to a. But if you run this in the console, you'll get a NameError. Why? Because a doesn't exist yet—you can't assign to something that doesn't exist.

To define a new variable, prefix it with \:

10 # \a

Now a exists and holds the value 10. The \ is a one-time thing: you only need it when you're creating the variable for the first time.

Terms: Monadic, Dyadic, Triadic...

Before we go further, let's get some vocabulary out of the way:

Reminder: No operator precedence. Left to right. Always.

Semicolon

Semicolons force an operator to behave monadically. You'll need this when a value appears right after a monadic operator.

Say you want the list [\2 3] using monadic -. You might try:

[2- 3]

But this gives [\1]. The interpreter sees 3 after - and assumes you want dyadic subtraction: 2 - 3 = \1.

To fix it, add a semicolon to tell - to stay monadic:

[2-; 3]

Now you get [\2 3]. The semicolon is like saying "stop, this operator is done, move along."

Code blocks

Like C, Polynomix uses {} for code blocks. A block runs its statements in order and returns the last value. Handy for grouping things together.

If statements

Use ? for conditionals:

cond ? block  $$ if(cond){block}
cond ? (val1 val2) $$ if(cond){val1}else{val2}
cond ? (val1 cond2 val2 val3) $$ if(cond){val1}else if(cond2){val2}else{val3}
...

Conditions and code blocks alternate, and the last item is always a code block. Once a true branch is found, the rest are skipped.

While loops

cond @ block

The dyadic @ repeats a block while the condition holds. If you make the condition a code block too, you get a do-while loop. Sneaky, but it works.

Boolean operators

Bool values are written as (1|) (true) and (0|) (false). Yes, the notation is unusual. Roll with it.

Boolean operators:

Comparison operators that return Bool:

These work on every data type. Yes, even Lists. Yes, even functions. We'll get there.

A challenge

Implement the Collatz conjecture. Here's the pseudocode:

n = 54
while n != 1
    if n % 2 == 0
        n = n / 2
    else
        n = 3 * n + 1
    end if
    print n
    print " "
end while

Hint: monadic <. prints a value. Use `blah' for strings.

Functions

Remember how there's no greater-than operator? That's because > is reserved for something better: functions.

The syntax is simple: arguments on the left of >, body on the right. The body can contain multiple statements, just like a code block.

(x > x + 1) # \increment

Call it with !, which turns a name into an operator:

5 increment! $$ 6

Multi-argument functions work too—they behave like operators:

(x y > x + y) # \add
5 add! 6  $$ 11
(t x y > 1 - t * x + (t * y)) # \lerp
0.5 lerp!(1 2)  $$ 1.5

Don't need a name? Skip the assignment and use the function inline:

5 (x>x+1)! $$ 6

Return statement

You've seen @ as a dyadic while loop. Monadically, it means "return this value from the function."

(x >
    x = 0 ? (1 @)         $$ If x is 0, return 1.
    x - 1 factorial! * x  $$ Recursive call (bad style).
) # \factorial

That factorial! inside the function works, but it's fragile—what if you rename the function? Use \. instead, which is the dedicated recursion operator:

(x > 
    x = 0 ? (1 @)
    x - 1 \. * x   $$ Recursion, the proper way.
) # \factorial

\. always refers to "this function." No matter what you name it, recursion just works.

A challenge

Rewrite the Collatz conjecture using recursion instead of a while loop.

Arrays

Arrays—called Lists in Polynomix—hold multiple values in order. Create one with square brackets:

[1 2 3]

Monadic , wraps a single value in a List. Stack them for extra nesting:

1,,,,  $$ [[[[1]]]] — it's Lists all the way down

Strings are just Lists of Chars, which means they're Lists all the way down too:

["h "e "l "l "o] $$ Same as `hello'

Operators on Lists

Lists have their own set of operators. Here are the essentials:

Types

Polynomix has 8 data types and a unique notation for types:

Structs

Beyond the 8 built-in types, you can create your own types using structs. A struct is simply a value with a label attached—no methods, no inheritance, no drama.

3,4 Point.

This creates a Point struct wrapping the pair (3,4). The period after the name is what triggers the struct construction. Use < monadically to unwrap it: 3,4 Point. < gives (3,4).

Structs are matched by name in the type system: $Point$ matches any Point struct. This lets you distinguish between, say, a Point and a Vector, even if they both wrap a Pair of numbers.

As the Polynomix wisdom goes: "A struct holds data with dignity; a class inherits only debt."

Type checking

Since types are first-class values, type checking is just a normal operation:

42 &           $$ Get the type: $+$
42 & $+$       $$ Is 42 an Expr? (1|)
`hello' & $["]$  $$ Is it a string (List of Char)? (1|)
42 & $[]$      $$ Is 42 a List? (0|)

The TypeString notation supports wildcards ($*$), unions (${+|[]}$ means Expr or List), and even capture groups for generic-style constraints. But that's advanced territory—save it for when you're comfortable with the basics.

Adverbs

Adverbs are modifiers that change how an operator behaves. You've already met one: !, which turns anything into an operator. But there are more.

The ranklift: \

We've seen \ used as a negative sign (\10 = -10) and to define variables (\a). But when placed before an operator, it becomes the ranklift adjective—it makes the operator broadcast over a List.

[1 2 3] \+ 10    $$ [11 12 13]
[1 2 3] \* 2     $$ [2 4 6]
[`apple'`banana'`orange'] \<   $$ [5 6 6] — each element's length

It works with functions too:

[1 2 3 4] \(x > x * x)!
$$ [1 4 9 16]

Apply/Reference: !

We've used ! to call named functions: 5 increment!. But it does more—it can reference any operator:

+! $$ The "+" operator as a function literal

Commute: !!

Double exclamation commutes an operator—it swaps (or duplicates) the operands:

3 -!! 5      $$ 2 — same as 5 - 3
10 /!! 2     $$ 1/5 — same as 2 / 10
3 *!!        $$ 9 — same as 3 * 3 (multiply 3 by 3)

This is handy when you want to use an operator "backwards" without rewriting your expression.

Trains

So far we've been writing functions the explicit way, with named arguments and >. But Polynomix has another trick: trains—a way to compose functions without naming any arguments at all. The idea comes from APL.

Write a train inside { }, using only operators or verb-noun phrases (i.e. an operator with a right operand but not a left one) - no named arguments:

[10 20 30] {~.+!/<}

This computes the average of the list: 20. Let's break down what's happening.

A train with two functions is called an atop. It takes one argument, feeds it through the right function, then passes the result to the left:

2 {+-} 5   $$ \7 - add, then negate

{f g} means: apply f first, then g. It's function composition: g(f(x)).

A train with three or more functions is called a fork. It takes two arguments (or more, if you have more branches), applies the outer functions to them independently, then combines the results with the middle function:

10 {/,%} 3   $$ 3,1 - pack quotient and remainder into a pair

{f g h} means: g(f(...), h(...)). The left and right branches process the same inputs, and the center merges their outputs.

Now back to the average: {~.+!/<}. This is a 3-function fork:

So the fork computes: sum(x) / length(x). That's the average, in 6 characters and zero named variables.

If you've used APL, you'll recognize this as (+/÷ρ). Polynomix extends the idea: a fork can have any number of branches, not just two. The center function receives as many arguments as there are branches.

Trains are not always the right tool—sometimes naming your arguments makes code clearer. But for short, punchy compositions, they're hard to beat.

Error Handling

Use : for try-catch. The dyadic form catches errors:

3 2! : ___
$$ If error throws, we catch it and return the string

Monadic : throws a value as an error:

`something went wrong' :
$$ Throws an error with the message

It's like try-catch, but with fewer keywords and more symbols. You'll get used to it.

More Fun with Lists

Generating Lists

There are several ways to generate Lists without typing every element:

5 =         $$ [0 1 2 3 4] — range from 0 to 4
2 =(1 10)   $$ [1 3 5 7 9] — range from 1 to 10, step 2

Working with Lists

[3 1 4 1 5] -     $$ [3 1 4 5] — remove duplicates
[3 1 4 1 5] +     $$ [2 3 0 1 4] — sort index (rank of each element)
[3 1 4 1 5] +. [2 3 0 1 4] =   $$ [1 1 3 4 5] - use the index to sort the elements
[1 2 3] /         $$ [3 2 1] — reverse
[1 [2 3] [4]] =   $$ [1 2 3 4] — flatten
[1 2 3 4] - 2     $$ [1 2],[3 4] - split at index 2

Reduce

Use ~. to reduce a List with an operator:

[1 2 3 4] ~. +!   $$ 10 — sum
[1 2 3 4] ~. *!   $$ 24 — product

And @. for prefix reduce (running totals):

[1 2 3 4] @. +!   $$ [1 3 6 10] — running sum

Putting It All Together

Fibonacci

(x >
    x < 2 ? (x @)
    x - 1 \. + (x - 2 \.)
) # \fib

10 fib!   $$ 55

Quicksort

(x>x<<2?(x@)x-1#\(h,,t)t\(y>h#;y<h/!!(1,0))!+.!!t\\./(1,1 h,,)=) # \qsort

A sorting algorithm in 63 symbols. Not bad for a language that doesn't even have a greater-than operator.

What Now?

Congratulations—you've made it through the tutorial! Here's what you can do next:

The best way to learn is to write code that breaks, stare at the error message, fix it, and repeat. You've got this.