The Taka programming language
Taka is a small, stack-based, functional programming language. It has two distinguishing features: it uses Polish (prefix) notation instead of reverse Polish (postfix) notation that is typical in stack-based languages, and it uses quotations (blocks of unevaluated code) to define functions and for control flow.
Taka is heavily inspired by Forth (of course), Joy and Factor (especially related to quotations), Clojure and Scala (functional things), and Rust (overall awesomeness).
With forward Polish notation and a small amount of syntactic sugar, programs can have similar readability to common applicative languages such as Python or Javascript. On the other hand, since Taka is stack-based and concatenative, programs can be very concise, and the language is simple to implement, with very little syntax.
The name "Taka" is Finnish for "back" or "behind", because Taka code is read backwards compared to most other stack-based languages.
Language tour
To give you a flavor of Taka, here are some small example programs. You can run these examples by saving them in a script file like fizzbuzz.taka and running it using one of the interpreters. You can also run them in a web-based interactive playground if that ever gets implemented.
NOTE: As the language is still work in progress, some or all of these examples may be out of date, or may use functionality that is not yet implemented. Read at your own risk.
range l[1 101]
map [
let $n
# push Fizz and/or Buzz on the stack
if-else [ n. mod 5. = 0 ] [ `Buzz' ] [ `' ]
if-else [ n. mod 3. = 0 ] [ `Fizz' ] [ `' ]
concat # combine the two strings on the stack
# if the string is empty, drop it and push n instead
if-else [dup. empty?] [ drop. n ] []
# print the result
show
]
The solution to Day 1 of Advent of Code 2015:
slurp `input.txt'
text/trim
map [if-else [= `('] 1 -1] graphemes
dup # store copy of mapped array to reuse across both parts
# part 1
show sum
drop
# part 2
scan-from 0 $+
index-of -1. + 1
show
You can see some more example code here.
Basic syntax and execution
Taka programs are read line-by-line, from right to left. A program consists of terms separated by whitespace. There are only three kinds of terms:
- literals: e.g. numbers or text
- blocks: terms surrounded by square brackets
[ ]which are quoted, i.e. not evaluated immediately - identifiers: names that refer to literal values, blocks, or built-in functions
Any text following a hash sign # is a comment and is ignored until the end of the line.
The central concept of Taka, like any stack-based programming language, is the stack. Literals are directly pushed onto the stack. For example, the program
1 2 3
pushes the numbers 3, 2, and 1 on to the stack. The number 3 is pushed first, as the program is read from right to left. At the end of the program, 3 is at the bottom of the stack, 2 is in the middle, and 1 is at the top. In general, stacks are written with the "top" on the left, and the "bottom" on the right.
The stack can be manipulated with functions, like +:
+ 2 3 4
# stack: 5 4
Here, 4, 3 and 2 were pushed on to the stack, and then the function + pops the top two items on the stack and pushes their sum, resulting in the stack 5 4.
Because taka programs are read line-by-line from top to bottom, the above program is equivalent to all of the following programs:
4
3
2
+
or
3 4
+ 2
or
2 3 4
+
Depending on context, the placement of line breaks may significantly affect the readibility of code.
Note that order-dependent arithmetic, such as subtraction and division, may seem backwards. For example, - 5 3 can be read as "subtract five from three", and results in -2. Similarly, / 3 6 produces 2 instead of 0.5. While this may seem a bit counter-intuitive, we will later learn about some syntactic sugar that allows writing these in a more natural way.
An important function is show, which displays (i.e. prints to standard output) the topmost item on the stack:
show 2 3 # prints 2
Note that the item stays on the stack. This can be used to inspect intermediate results while debugging:
2 3 4
show # prints 2
+
show # prints 5
*
show # prints 20
Some basic stack manipulation operations include dup, which duplicates the top item and swap, which swaps the order of the two topmost items.
2 dup 3
# stack: 2 3 3
swap
# stack: 3 2 3
-
# stack: -1 3
*
# stack: -3
Text
Text literals begin with a backtick ` and end with an apostrophe '. The program
`My hovercraft is full of eels'
pushes the text "My hovercraft is full of eels" onto the stack. Text may contain line breaks:
`My hovercraft
is still
full of eels'
Text is UTF8-encoded Unicode, and can be broken into "characters" using the graphemes function or into bytes using the bytes function.
Blocks
A block is code surrounded by square brackets [ ], which is pushed on to the stack as a single value. Importantly, the content of a block is not evaluated immediately:
[* 2]
# stack: [* 2]
This is a function that multiplies an item on the stack by two. Just writing * 2 without the brackets would be an error, since * requires there to be two items on the stack to be multiplied.
Blocks are the only way to define functions in Taka. Most Taka programs consist of functions combined with higher-order functions, called combinators, which take one or more blocks from the stack and apply them to other items on the stack in various ways.
Lists
A block may be converted to a list using the list function:
list [1 2 3]
# stack: list [1 2 3]
Note that when a block is converted to a list, its contents are evaluated:
list [+ 1 2]
# stack: list [3]
The letter l is a shorthand for list. List literals are often written using the shorthand, and without a space before the bracket:
l[1 2 3]
# stack: list [1 2 3]
Lists may be indexed using the at function. Indexing starts at zero.
l[`apple' `banana' `coconut']
# stack: list [`apple' `banana' `coconut']
at 1
# stack: `banana'
Combinators
Combinators are functions that apply blocks to items on the stack, often lists. One of the most common cobminators is map, which takes a block (a function) and a list, and applies the function to each item in the list:
map [* 2] list [1 2 3]
# stack: list [2 4 6]
Another useful combinator is filter, which takes a function and a list, and only keeps items for which the function evaluates to true:
filter [< 0] l[-1 2 -3 4]
# stack: list [-1 -3]
Often, the list being processed is written first on its own line, followed by the combinator and function. More complex function blocks may be written on multiple lines. Because a combinator leaves the resulting list on the stack, combinators may be chained by writing them one after another.
l[`apple: sour' `banana: sweet' `mango: sweet' `yuzu: sour']
filter [
text/split `: '
last
= `sour' # keep only the sour fruits
]
# stack: list [`apple: sour' `yuzu: sour']
map [first text/split `: ']
# stack: list [`apple' `yuzu']
Definitions using let
The let function allows one to define names for variables or functions. The top item on the stack should be an identifier, and the second item the value. Because code is executed strictly from right to left, and the identifier may be undefined before the let function is executed, the identifier needs to be quoted using a block:
let [x] 3
# stack:
x
# stack: 3
+ x
# stack: 6
Note that defining a variable using let removes the value from the stack.
Identifiers referring to functions are immediately evaluated on the stack:
let [negative?] [< 0]
# stack:
negative? 5
# stack: false
negative? -123
# stack: true false
Identifiers are scoped: an identifier defined inside a block is un-defined (or reverts to a previous definition) when a block ends:
let [number] 42
l[1 2 3]
map [
let [another-number] 43
let [number] 99
show number # prints 99
# the stack is now 99 and the number from the list we're mapping over
# take their sum
+
# add another-number, for good measure
+ another-number
]
# stack: list [143 144 145]
# number is now back to its previous value of 42, and another-number is no longer defined
number
# stack: 42 list [143 144 145]
Unlike in many other languages (but like in Lisp, for example), identifiers may contain almost any non-whitespace character, excluding backticks `, square brackets [ ], as well as the dot . and the dollar symbol $, which have special meanings discussed below. For example, some-variable, päivä, empty?, and text/split are all valid identifiers.
Syntactic sugar
We have seen basically all of the syntax of Taka. In addition to basic syntax, Taka has two pieces of syntactic sugar, to make writing programs a bit more pleasant.
Dot
The dot . splits a line into segments that are evaluated left-to-right instead of right-to-left:
4 . 2 3 . 1
# stack: 1 2 3 4
This allows writing binary operations as effectively infix notation:
5 . - 3
is equivalent to
- 3 5
which evaluates to 2.
Whitespace around dots is flexible. This makes it possible to write code that looks like "method chaining":
`hello world'.text/split ` '.map [reverse]
# stack: list [`olleh' `dlrow']
However, it's more idiomatic to write this with the dot immediately following the term on the left, with a space after it. This way, the code reads a bit like a series of commands:
`hello world'. text/split ` '. map [reverse].
Dollar
The dollar symbol $ is a short-hand for writing single-element blocks: $x is the same as [x]. This is useful with let or simple functions in combinators:
let $x 123
l[1 2 3 4]
filter $even?
# stack: list [2 4]
A dollar followed by a space is the same as the empty block [], or the identity function. This is most commonly used with count to get the length of a list:
l[1 2 3]
count $
# stack: 3
Error handling
For now, your program will just crash if there is an error. Sorry.
Documentation
Maybe some day. For now, take a look at the source.
Implementation
There are two interpreters for Taka:
- A Python-based interpreter, which is/was used for quick prototyping. It is extremely slow and also outdated.
- A Rust-based interpreter, which is a fairly straightforward translation of the Python interpreter to Rust. It is around 10x faster than the Python version.
For now, there is no compiler or bytecode VM or anything fancy like that, just straightforward execution of terms one by one as they are encountered.
FAQ
Questions that have definitely been asked by actual people.
Why?
I wanted to create a programming language. I actually want to create a language that is not as esoteric as a stack-based one, but this is a great way to get started because it's so simple.
I also came up with the forward Polish notation and dot syntax idea, and thought it was neat enough to try and implement.
The syntax feels like it lends itself to interactive REPL-driven development, which I want to explore in the future. I want to implement a Jupyter kernel for my language, and hopefully use it for some simple data analysis.
The primary use case for Taka is currently to solve problems in Advent of Code 2025, see here. Taka aims to be a general-purpose language, not some crazy golfing thing. But still, Advent of Code provides great example tasks to solve with a simple language, and, more importantly, it set a deadline for getting the language working.
Is it fast?
Considering that there's no optimization, it kind of is? At least some of the Advent of Code solutions seem to run about as fast as a naive Python solution, so that's the ballpark. Or I might just be doing the benchmarking wrong.
Is there syntax highlighting for my editor???
No. For now, I use set syntax=conf in vim, which seems to be enough to dim comments, and causes only minor issues with strings. For the markdown in this README I use bash because conf doesn't seem to work, it's kind of wonky but good enough.
License
All code in this repository is copyright 2025 András Márton Gunyhó, licensed under the GNU General Public License v3, see LICENSE.