Beginners' Page
Welcome to the landing page for Jelly beginners! This resource is intended to guide you from your very first encounter with Jelly to a point where you can practice solving problems, self-study, and seek help through other channels.
-
Conventions and Terminologylink
A couple of terms and conventions are important to establish and will be used throughout much of this page and this entire website:
- Arity refers to the number of arguments that a function takes. A niladic function (nilad) takes zero (0) arguments, a monadic function (monad) takes one (1) argument, and a dyadic function (dyad) takes two (2) arguments.
- Atoms are Jelly's name for functions. These take values as arguments and produce values. Note that every atom in Jelly has fixed arity, meaning that the number of arguments it takes does not change depending on context. Quicks can produce links with different arities than the base link, but the atom itself has fixed arity.
- Quicks are Jelly's name for operators / meta-functions. These consume some number of links that come before them and modify them or produce some other link.
- Links are lines of code in Jelly, similar to how one can define multiple functions in one file. The last link (separated by newlines or
¶
) is the "main link" and is executed when the program is run. - Links also refer to any single function, whether it is an atom or the result of composing multiple atoms together with (a) quick(s). This terminology is a bit confusing, but it is the official naming scheme.
- Chains are groups of links written side-by-side. The functionality of chains depends on its arity and the chaining rules, which will be explored later.
Throughout this section, we will represent nilads with digits, monads with uppercase letters, and dyads with mathematical operators, such as
+ × ÷
.Jelly is 1-indexed, meaning that the first index in a list is
1
and the last index is the length. Index access in lists wraps around, so0
refers to the last element, and index5
in a length-3 list points to the second value. -
Tacit Programminglink
Tacit programming, also known as point-free programming, is a paradigm where functions do not specify the arguments to which they apply or explicitly refer to their arguments. Instead, functions compose together to form more advanced combination functions. In Jelly, everything is a function (link) in the final parsed code, and its structure / order of evaluation and the arguments to which each function applies is determined by the arities. (Unlike APL, a practical tacit language, rather than determining the arity of a function via the structure, Jelly determines the structure based on the arity.)
Jelly has nilads available to access the command line arguments and the link's arguments. Using these usually results in a less golfy program, but moreover, it is not idiomatic to tacit programming, and it is good practice to avoid them and use the tacit structure to your advantage. There are cases where they will save bytes and be the optimal solution, but generally, it is good to avoid them.
Let us review a very basic example:
C+H
.C
is a monad which takes a numberx
and returns1 - x
.+
is just addition (dyad).H
is a monad which halves its argument; givenx
, it returnsx / 2
. We will run this link monadically as an example, usingx
as the generic argument. The value of the chain begins at the left argument (this is not always true, but we will get into that later on). The first pattern matched is theC
monad. The value of the chain becomes1 - x
. Next, the pattern matched is+H
, which is a 2,1-chain that composes+
andH
to add (+
) the left argument,1 - x
, to the right argument, which isH
applied tox
(the halve atom applies to the left argument of the link, not this sub-chain, so it applies tox
and not1 - x
). Don't worry too much if this is confusing for now.Therefore, overall, given
x
, it first computes1 - x
, and then computes(1 - x) + (x / 2)
or1 - x / 2
. We never need to refer tox
anywhere in our program, but rather roughly write out "take the complement and add it to half", and the functions compose together - that is the beauty of tacit programming. -
Your First Programlink
Let's begin with a very simple program - Hello World. To get started, head over to the online Jelly executor on this site or Try It Online! (created by Dennis).
In Jelly, strings begin with
“
and end with”
. So, the most basic solution would be this:“Hello, World!”
If a string is not terminated at the end of the program, it is implicitly closed, so you can drop the last
”
. However, there is a shorter way to outputHello, World!
by using dictionary-compressed strings. You can read a more thorough explanation on that page. The rough summary of how it works is if a string is terminated using»
, its contents are converted to a base-250 integer and decompressed using a specific algorithm. This tends to be much shorter for strings of mostly English text that can be encoded into dictionary entries; however, for more random strings, it can end up taking more characters. In this case, the shortest way to solve Hello World is to use a compressed string:“3ḅaė;œ»
Don't worry if the exact algorithm doesn't make sense, and you definitely shouldn't try to learn how to compress strings yourself. The string compression tool allows you to do that easily.
-
Jelly Syntaxlink
Other than atoms and quicks, there are a few syntactical components in Jelly. Spaces can be used to separate tokens where necessary (for example, to have
1 2
as two literal numbers, rather than12
). Numbers are represented with digits, decimal points, minus signs (note that subtraction uses_
because-
is reserved for numeric literals),ı
, andȷ
. A regular number consists of an optional minus sign, followed by optional digits, followed by an optional decimal point, followed by optional digits (but must not be empty). If a minus sign is present with nothing after it, it represents-1
. If a decimal point does not have digits to its left, it defaults to0
, and the right side defaults to5
(so1.
is1.5
and.3
is0.3
). (Exercise: what does-.
evaluate to?)An exponential number literal is formed using
ȷ
, with optional regular number literals to its left and right. If the left is missing, it defaults to1
, and if the right is missing, it defaults to3
.xȷy
evaluates tox × 10 ^ y
. So,4ȷ
evaluates to4000
,ȷ2
is 100, and.ȷ.
is1.5811388300841898
. A complex number literal is formed usingı
, with optional regular or exponential number literals to its left and right. The left side is the real argument, defaulting to0
, and the right side is the imaginary argument, defaulting to1
.2ı
evaluates to2 + i
,ı4
is4i
, and1ȷ2ı3ȷ4
is100 + 30000i
. Leading zeroes are parsed as separate tokens; for example,00103
is identical to0 0 103
.Regular string literals begin with
“
. When this is encountered, Jelly begins scanning a string literal. Encountering another“
while parsing will convert it to a list of strings, where each section starting with“
is an element. To terminate a normal string, use”
. If the end of the file is encountered without seeing a string terminator, this is the default. This returns the string as-is, so“Hello!”
(and“Hello!
if nothing follows it) returns"Hello!"
, and“Hello“World”
returns["Hello", "World"]
(which is printed by just outputting them back-to-back by Jelly - you can useŒṘ
, the Python's String Representation monad, to see what the internal representation is). Note that strings don't actually exist in Jelly, they are just lists of characters. You can get real strings by adding or multiplying characters, but they will be treated as singleton values and cause various problems. There are rare situations where this can be golfier, but it will usually just cause confusion.»
terminates a dictionary-compressed string (the type we used in Hello World in the section above). This is done using thesss
function, an algorithm designed for decompressing strings for golfing.‘
terminates a code-page index list (sort of like callingord()
on each character, but with Jelly's code-page rather than unicode). This returns a list of integers from0
to249
, and if the literal was a list literal (i.e. multiple“
s are present), it returns a list of lists. For example,“¡ẓ“ạ¶‘
evaluates to[[0, 229], [211, 127]]
.’
terminates a base-250 number, where¡
represents1
andż
represents250
(having0
wouldn't be very useful because then leading zeroes result in the same number and thus a small set of numbers would take an extra byte to represent). For example,“¡£Ẇ“Ḍż“M{’
evaluates as[63458, 43750, 19624]
.«
is reserved as a string terminator but is currently unimplemented and just acts like”
.⁾
begins a two-character string literal. This is very simple -⁾ab
evaluates to"ab"
(technically,["a", "b"]
- remember that Jelly uses lists of characters for "strings").⁽
begins a two-character base-250 number. This is decoded the same way as“ab’
. However, once this number is evaluated, if it is greater than31500
,62850
is subtracted, and otherwise,750
is added. This means numbers from-31349
to-100
and1001
to32250
can be represented (being able to represent three-digit numbers would not be useful, and1000
can be represented byȷ
).If multiple literals are written adjacently separated by
,
(with no extra spaces), they become a list. Note that,
is also the pair dyad, and if there are spaces around the comma or it only has a literal on one side, it will act as such. You can also use square brackets to nest lists (but they must be balanced); for example,[1,2],[3,[4,5]],6
evaluates to[[1, 2], [3, [4, 5]], 6]
, and[1, 2]
errors because the space causes it to be split up into[1
(unbalanced),,
, and2]
(unbalanced).Once a literal has been parsed, it is equivalent to a constant nilad that returns that value.
Newlines and pilcrows (
¶
) are the same (they are translated to the same byte internally) and they can be used to separate links. It is convention to use newlines to separate links and pilcrows in literals where you want codepoint 127. For example, the following code consists of three links:link 1 link 2 ¶ link 3 “ ¶ this is still part of link 3 ”
Only the last link is run by default, and it can refer to other links to call on them, sort of like defining multiple helper functions and a main function in C++.
Finally,
ø
,µ
,)
,ð
, andɓ
are chain separators. We will explore those later, as they get quite complex. -
Vectorizationlink
Before we continue on to Jelly-specific chaining rules and program structure, let's introduce vectorization, a common concept in array languages and golfing languages. Multiplying two lists together doesn't work in most languages (e.g. in Python,
[1, 2, 3] * [4, 5, 6]
will give an error). However, one behavior that would make sense is to multiply each corresponding pair of elements, so[1, 2, 3] * [4, 5, 6]
could give[4, 10, 18]
. Try the following online:1,2,3 × 4,5,6
Many built-ins in Jelly vectorize. For example,
¬
(logical NOT) will vectorize down to each individual value, so-,0,1 ¬
yields[0, 1, 0]
, and[1,0],[0,1] ¬
yields[[0, 1], [1, 0]]
. Some monads will explicitly say that they do not vectorize; for example,Ṇ
(logical NOT) is the non-vectorizing form and will yield0
if given[-1, 0, 1]
as an argument. Some built-ins will vectorize partially; for example,U
(upend / reverse) reverses each list, so1,2,3 U
yields[3, 2, 1]
and[1,2,3],[4,5,6] U
yields[[3, 2, 1], [6, 5, 4]]
.Vectorization works relatively intuitively for dyads. If the left and right arguments are both single values, it just applies the function to the values. If one argument is a list and the other is a single value, it loops over the list side. For example,
1,2,3 + 4
gives[5, 6, 7]
and1 + 2,3,4
gives[3, 4, 5]
. If both arguments are lists, then it applies to each corresponding pair. If one list is longer than the other, the trailing elements are included at the end unmodified (this behavior is different to Python'szip
, which cuts off extra elements, and APL/J's vectorization, which errors if the axis lengths mismatch). For example,4,5,6 × 2,3,4,5,6
gives[8, 15, 24, 5, 6]
.Some dyads only vectorize on one side. For example,
ị
gets the element in the right argument using the left argument as an index, so if the left argument is a list, it will vectorize, but the right argument stays as a list.2,1,4 ị 7,9,4,6,8,5
gives[9, 7, 6]
, and[5,2],[3,4] ị 7,9,4,6,8,5
gives[[8, 9], [4, 6]]
. Vectorization will make more sense with examples, and many built-ins work with many unique vectorization methods.When something is said to vectorize, it usually means to individual single elements. Vectorizing to depth 1 means that the built-in will process flat lists, and keep vectorizing down until it reaches depth 1 lists. Likewise, vectorizing to depth 2 means the built-in processes matrices.
-
Niladic / Monadic Chainslink
The main link is run with its arity determined by the number of arguments. We will begin by exploring niladic and monadic chains together, as they work nearly identically. If a chain is run niladically, there are two possibilities. If it begins with a nilad, the remainder of the chain is evaluated monadically on that argument. Otherwise, the chain is evaluated monadically at zero. So,
13 + 4 H
is equivalent to+ 4 H
evaluated with an argument of13
, and+ H _
is equivalent to the same link evaluated monadically on0
.To evaluate a monadic chain, we have two starting situations. If the link begins with a nilad, it is popped off and becomes the current value. Otherwise, the current value begins as the chain's argument.
Then, we can walk down the chain and match patterns of links as we go, slicing off each match, evaluating it, and setting the current value to the new value. We will represent nilads with digits, monads with uppercase letters, and dyads with mathematical operators, such as
+ × ÷
. The left argument will be referred to asα
, and the current value will beλ
. For monadic chains, these are the patterns (the first one matched is applied):Pattern New λ
valueArities / Name + F ...
λ + F(α)
2,1-chain + 1 ...
λ + 1
2,0-chain / dyad-nilad pair 1 + ...
1 + λ
0,2-chain / nilad-dyad pair +
λ + α
Lone Dyad F
F(λ)
Lone Monad If a nilad is encountered that isn't part of a dyad-nilad or nilad-dyad pair, it causes the current
λ
value to be output (with no trailing newline) andλ
then becomes the value of that nilad. This is known as smash-printing - essentially, the chain's current value is forced to a new value and the old value is outputted and discarded. -
Leading Constant Chains (LCC)link
There is a special type of chain known as a "Leading Constant Chain". As the name describes, it is a chain with a leading constant (or any nilad, it doesn't actually have to be a constant nilad). The formal definition of an LCC is chain whose first link is a nilad and whose subsequent links are either monads, dyad-nilad pairs, or nilad-dyad pairs. Essentially, its link arities should match the pattern
0(1|20|02)*
. Since they begin with a nilad and then only contain monadic-like components, they don't apply a function to anything else and are self-contained, so parsing them into one chain would result in essentially a nilad. Thus, some things interact differently when LCCs are present. -
Dyadic Chainslink
Dyadic chains have two arguments (we will call them
α
(left) andω
(right)), and we evaluate them similarly with a current value ofλ
. There are a few more rules for determining what the starting value is:- if the chain starts with three dyads, e.g.
+ × ÷
, then the initial value isλ = α + ω
, and we consider the rest of the chain,× ÷ ...
- if the chain is an LCC starting with some constant (nilad)
κ
(i.e.κ F 1+ +1 ...
), then the initial value isλ = κ
, and we consider the rest of the chain - otherwise,
λ = α
, and we consider the whole chain
The patterns are different for dyadic chains. Again, we will represent nilads with digits, monads with uppercase letters, and dyads with mathematical operators, such as
+ × ÷
.Pattern New λ
valueArities / Name + × 1 ...
(λ + ω) × 1
*2,2,0-chain + ×
λ + (α × ω)
2,2-chain + 1 ...
λ + 1
2,0-chain / dyad-nilad pair 1 + ...
1 + λ
0,2-chain / nilad-dyad pair +
λ + ω
Lone Dyad F
F(λ)
Lone Monad * The 2,2,0-chain rule only applies if the nilad is part of an LCC; that is, the nilad is not followed by a dyad.
Like with niladic / monadic chains, if a nilad is encountered that is not part of one of these rules, it smash-prints the current value of
λ
and setsλ
to the new nilad's value. - if the chain starts with three dyads, e.g.
-
Multi-Chain Links (Chain Separators)link
A link (the outer link, not each individual unit) is not actually a single chain that consists of a bunch of links (the unit type) that chain together according to the rules. Each link is actually a chain of chains; however, the outer chain usually has unit length (only contains one sub-chain). Recall the chain separators from earler,
ø
,µ
,)
,ð
, andɓ
. The first chain (and often the only one) is variadic by default, meaning that its arity is based on context. By default, this means that the first chain has the arity of the link itself, which means that if no chain separators are present, it behaves quite normally - a dyadic link will evaluate its chain dyadically, a monadic link will evaluate its chain monadically, etc. These chain separators start a new chain. You cannot nest chain separators - they don't act as sub-functions, but rather separate a link into multiple chains. If there are links before the first chain separator, it becomes a variadic chain; otherwise, it just gets removed (so1µ2
is a variadic chain followed by a monadic chain, whereasµ2
is just one monadic chain). Let's look at what each separator means:ø
starts a new niladic chainµ
starts a new monadic chainð
starts a new dyadic chainɓ
starts a new dyadic chain which swaps its left and right arguments)
is an alias forµ€
- it creates a new monadic chain and maps over the previous one
The last separator introduces a new concept - applying quicks to chains. Recall that a quick pops zero or more links and modifies them.
€
takes one link and maps it over its left argument; for example,[1,2,3],[4,5] L€
returns[3, 2]
(Length of Each) and1,2,3 ,€ 4,5,6
returns[[1, [4, 5, 6]], [2, [4, 5, 6]], [3, [4, 5, 6]]]
(Pair Each). However, if there are no links left in the chain, it doesn't necessarily error - if there is another (inner) chain in the (outer) chain, it will pop that chain off instead. The chain keeps the arity it was assigned based on the separator it started with, and if the chain popped is variadic, its arity will more or less be determined by the nature of the quick (for example,/
(reduce) works with dyads, so a variadic chain is evaluated dyadically regardless of the link's arity). So,ð , µ /
starts a dyadic chain, adds,
(pair) to it, starts a new monadic chain, and then reduces. Since there are no links in the current chain, it pops the last chain instead. Thus, this snippet creates a monadic chain which reduces over the dyadic chainð,
(it is functionally equivalent toµ,/
).Chaining rules apply to both how the outer chains are grouped together and how the links within each sub-chain are grouped. For example,
ð,µ,
gives[1, [1, 1]]
when run monadically (with an argument of1
) because of the 2,1-chain rule, but gives[[1, 2], [1, 2]]
when run dyadically (with arguments1
and2
) because the 2,1-chain rule does not apply to dyadic chains and instead just appliesð,
to the arguments, followed byµ,
to the results. -
Further Resourceslink
Congratulations! You've reached the end of the beginners' tutorial page. Hopefully, you should have a decent idea of what to expect going forward and the fundamentals of Jelly's structure and functionality. At this point, practice and experience is the best teacher - you can only learn so much by reading. There are exercises available on the official JHT website. You can also try your hand at solving harder problems on Code Golf Stack Exchange.
If you have any questions, feel free to ask in Jelly Hypertaining - you'll need explicit write access to the chat room, so request access and explain that you're interested in learning Jelly or have a specific question if anyone asks why you want access.