We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
43
43 is an esoteric programming language by User:Dragoneater67 made in 2026. It was designed to be very hard to implement. The name is derived from a decimal-to-hexidecimal conversion (43 is 67 in hexadecimal).
Overview
The language is high-level, it is in some ways quite similar to some non-esoteric programming languages (though it is still mostly esoteric). All data is stored in variables, the global deque, global queue, and the global stack. At the start of execution a fullscreen window is created where the program can draw. The window is initialized to be plain white.
Built-in types
43 has many built-in types, they're all immutable.
int8-- Signed 8-bit integer.int16-- Signed 16-bit integer.int32-- Signed 32-bit integer.int64-- Signed 64-bit integer.int128-- Signed 128-bit integer.bigint-- Signed unbounded integer.uint8-- Unsigned 8-bit integer.uint16-- Unsigned 16-bit integer.uint32-- Unsigned 32-bit integer.uint64-- Unsigned 64-bit integer.uint128-- Unsigned 128-bit integer.ubigint-- Unsigned unbounded integer.float-- Signed 32-bit floating point number.double-- Signed 64-bit floating point number.bigfloat-- Signed arbitrary precision floating point number.ufloat-- Unsigned 32-bit floating point number.udouble-- Unsigned 64-bit floating point number.ubigfloat-- Unsigned arbitrary precision floating point number.complex-- Complex number.bool-- Boolean.char-- Character.string-- String.func-- Function.type-- Type.
Variables
Variables are used for storing data. This syntax is used for variable definition:
var name<type> = value
Between var and name, some keywords can be added to define the variable's properties:
mutmakes the variable mutable, andimmutmakes the variable immutable.localmakes the variable local to its scope, andglobalmakes the variable global.
Variables are local (with the exception of top-level variables, which are always global) and mutable by default. Mutable variables can skip intialization:
var name<type>
Mutable variables can omit type for dynamic typing:
var name
Variables can be accessed using their names:
name
And they can be reassigned:
name = value
Variable names can only contain uppercase and lowercase letters, numbers and underscores, they cannot start with numbers.
Arrays
Arrays are defined similarly to variables:
var name<type>[length] = { values }
Values inside arrays are comma separated. Length can be omitted if values are initialized:
var name<type>[] = { values }
Initialization can be skipped for arrays, but that requires specifying length:
var name<type>[length]
Arrays can be accessed and reassigned like variables. An element of an array is accessed like this:
name[index]
And is reassigned like this:
name[index] = value
Functions
There are 2 types of functions: anonymous functions and named functions.
Anonymous functions
Anonymous functions are defined with this syntax:
lambda(arguments) => returntypes {
code
}
Arguments are separated by commas, each argument looks like this:
name<type>
Return types are also separated by commas, and they're just type names:
type
Functions can omit return types if they don't return anything:
lambda(arguments) {
code
}
Functions can also omit arguments along with parentheses if they dont take arguments:
lambda {
code
}
Arguments can omit types to accept any type:
name<>
Keywords mut/immut are applicable to arguments too:
mut name<>
Arguments are immutable by default.
Each rettype must be substituted with ? if the return type can be anything.
Functions return values using the return keyword:
return values
Here, values are comma separated. Functions can be called:
(function)(arguments)
Return values can be captured by assigning them to variables:
variables = function call
Here, variables are comma-separated.
Named functions
To create a named function, assign an anonymous function to a new variable:
var name<func> = function
Keywors that are applicable to variables are applicable to named functions to:
var global immut name<func> = function
A function can be assigned to a pre-existing variable if its of type func or dynamic:
name = function
A named function can be called:
name(args)
Types
Types can be created with this syntax:
var base<base type> name<type> = {
variables
}
Here variables are comma separated. They represent properties of a type. Keywords global and local do not apply to properties. There are 3 property-specific keywords:
publicmeans that it can be accessed from outside of the type's property functions.privatemeans that it can't be accessed from outside of the type's property functions.inheritmeans that the property gets inherited by types that are based on the owner type.
Keyword base<base type> can be omitted if the type isn't based on any other type. Properties are public and inheritable by default
Here's an example property:
var private inherit name<type> = value
Properties can be functions too:
var public inherit name<func> = function
There are some property function names that have special meanings:
_init(this<typename>, value<>)called when an initialized variable of its type is created (var name<typename> = value). This function is supposed to initialize the variable, Here, the newly created variable of type typename (which is currently uninitialized) is passed as the first argument, and value is passed as the second argument._initnoval(this<typename>)called when an uninitialized variable of its type is created (var name<typename>). This function is supposed to initialize the variable, Here, the newly created variable of type typename (which is currently uninitialized) is passed as the first argument._opoperatior(this<typename>, other<typename>) => ?defines the behaviour of a dyadic operator. Here, operator is replaced by an operator (the list of which will be mentioned later). The left operand is passed as the first argument, and the right operand is passed as the second argument. For example,a + bis the same asa._op+(b)._opoperatior(this<typename>) => ?defines the behaviour of a moandic operator. Here, operator is replaced by an operator (the list of which will be mentioned later). The operand is passed as the first argument. For example,^ais the same asa._op^().
Operators
Only applicable to numbers.
a - b-- Subtractbfroma.a / b-- Divideabyb, round the result toward 0 ifaandbare integers.a * b-- Multiplyabyb.a > b-- Check ifais greater thanb.
Only applicable to booleans:
a ^ b-- Exponentiateabyb.a & b-- Logical ANDaandb.a | b-- Logical ORaandb.^a-- Logical NOTa.
Only applicable to strings and numbers:
a + b-- Addatob.
Applicable to all types:
a == b-- Checkaandbfor equality.
Operations are executed in random order.
Control flow
The language has if/else statements, for loops and while loops (which also support else clause).
If statements
If statements use this syntax:
if condition -> lambda {
code
} else -> lambda {
code
}
This means that if condition is true, the former lambda function is called, else, the latter lambda function is called. Since the condition point to functions, they can use named functions instead of anonymous ones:
if condition -> func1 else -> func2
This means that if condition is true, func1 is called, else, func2 is called. Else clause may be omitted if it is unused:
if condition -> func
This means that if condition is true, func is called, else, nothing happens. The condition can be omitted too:
if -> func
This calls func unconditionally.
While loops
While loops use a syntax that is very similar to if statements:
while condition -> lambda {
code
} else -> lambda {
code
}
This means that the former lambda function is called in a loop while condition is true, and if condition was not true during the first check, the latter lambda function is called. Similarly to if statements, anonymous functions can be replaced with named ones and the else clause and even condition can be omitted. If condition is omitted, the function will be executed in an infinite loop.
For loops
For loops are used to iterate over elements in an array, they use this syntax:
for array -> lambda(immut el<>) => ? {
code
}
Here, each element of the array is passed to the lambda function sequentially, the return value of the lambda function is the new value of the respective element. If array is immut, return types must be omitted:
for array -> lambda(immut el<>) {
code
}
Just like if statements and while loops, the lambda functions shown in the examples can be replaced with named functions. Strings can be used in place of arrays.
Booleans
Booleans are reperesented by either of these 2 keywords:
true
or
false
Numbers
Numbers are written in base-6 with this alphabet:
012345
Floats use . for the point:
0.3
Floats can be written as fractions using the x//y notation:
1//2
The example above is equivalent to 0.3 in base-6. Number types with a bound wrap on overflow. All integer literals are bigint and all float literals are bigfloat.
Strings and Characters
Strings are wrapped in single quotes:
'This is a string.'
Characters are wrapped in double quotes:
"4"
Strings and characters use URL-encoding for escaping characters:
'This string ends with a newline.%0a'
Characters of a string can be accessed like elements of an array:
string[index]
Characters of a string can be reassigned too:
string[index] = value
Conversions
All conversions must be explicit. Only conversions from the table below are legal:
| From | To | Note |
|---|---|---|
| Bigger number types | Smaller number types | Truncates the number if needed. |
char |
string |
|
Any number type other than complex |
complex |
|
| Integers | Floating point numbers | Truncates if needed. |
| Floating point numbers | Integers | Truncates if needed, rounds to nearest nearest 0. |
Comments
Comments are wrapped in `:
`This is a comment.`
Comments can stretch across lines:
`This is a multi line comment.`
Errors
All errors kill the program and all of its threads immediately. There's only 1 type of error:
Something went wrong.
Which occurs when something goes wrong.
Preprocessor
Preprocessor commands take up an entire line. They are structured like this:
%command args
Here, args are space-separated. Upon evaluation, the command's return value replaces the command itself. Here's a list of preprocessor commands:
use filepath-- Reads the file at the filepath and returns its contents.def name(args) definition-- Similar todefinefrom C. Creates a macro named name (the name can only contain uppercase latin letters, numbers and underscores, cannot start with numbers), with comma separated args, and definition, where each instance of an argument name gets replaced by the value passed to the macro.ifdef name { code }-- Runs code if a macro named name exists.ifndef name { code }-- Runs code if a macro named name does not exist.undef name-- Delete a macro named name.
A preprocessor macro can be used like this:
NAME(args)
Here, args are comma-separated.
Built-in functions
43 has many built-in functions, they're all immutable.
Types
typeof(immut value<>) => type-- Get the type of a value.typeconv(immut target<type>, immut val<>) => ?-- Convert a value to an another type.strlen(immut str<string>) => bigint-- Get the length of a string.arrlen(immut arr<>[]) => bigint-- Get the length of an array.
Data
inject(immut element<>)-- Inject an element into the global deque.push(immut element<>)-- Push an element into the global deque.pop() => ?-- Pop an element from the global deque.eject() => ?-- Eject an element from the global deque.enqueue(immut element<>)-- Enqueue an element into the global queue.dequeue() => ?-- Dequeue an element from the global queue.spush(immut element<>)-- Push an element into the global stack.spop() => ?-- Pop an element from the global stack.
Control flow
die()-- Halt the program.thread(immut function<func>)-- Create a new concurrent thread that executed the passed function.wait(immut seconds<bigfloat>)-- Wait a few seconds.exec(immut code<string>)-- Execute code.append(immut line<bigint>, code<string>)-- Append code after a specified line, if the line is less than or equal to 0, the code gets prepended to the program.proglen() => bigint-- Get program length in lines.
Graphics and audio
getw() => bigint-- Get screen width.geth() => bigint-- Get screen height.drawp(immut x<bigint>, immut y<bigint>, immut h<bigfloat>, immut s<bigfloat>, immut l<bigfloat>)-- Draw a pixel on the screen. Color is specified using the HSL color format.beep(immut hz<bigfloat>, immut seconds<bigfloat>)-- Beeps for a few seconds.
Examples
Infinite loop
var entry main<func> = lambda(immut args<string>[]) => uint8 {
while -> lambda {}
return typeconv(uint8, 0)
}
XKCD Random Number
var entry main<func> = lambda(immut args<string>[]) => uint8 {
var width_too_short<bool> = getw() > 3
width_too_short = ^width_too_short
var height_too_short<bool> = geth() > 3
height_too_short = ^height_too_short
if width_too_short | height_too_short -> die
drawp(0, 0, 0, 0, 0)
drawp(2, 0, 0, 0, 0)
drawp(0, 1, 0, 0, 0)
drawp(1, 1, 0, 0, 0)
drawp(2, 1, 0, 0, 0)
drawp(2, 2, 0, 0, 0)
while -> lambda {}
return typeconv(uint8, 0)
}
Computational class
43 is Turing-complete since it natively supports the lambda calculus. Here is the lambda expression (λx.xx)(λx.xx) written in 43 as an example:
var entry main<func> = lambda(immut args<string>[]) => uint8 {
(lambda(x<func>) => func { return x(x) })(lambda(x<func>) => func { return x(x) })
return typeconv(uint8, 0)
}