Primitive Types
Aiken has 6 primitive types that are built in the language and can be typed as literals: booleans, integers, strings, byte arrays, data and void. The language also includes 2 base building blocks for associating types together: lists and tuples.
Worry not, we'll see later in this manual how to create your own custom types.
Inline comments are denoted via //
. We'll use them to illustrate the value
of some expressions in examples given all across this guide.
Bool
A Bool
is a boolean value that can be either True
or False
.
Aiken defines a handful of operators that work with booleans. No doubts that they'll look quite familiar.
Operator | Description |
---|---|
&& | Logical conjunction (a.k.a 'AND') |
|| | Logical disjunction (a.k.a. 'OR') |
== | Equality |
! | Logical negatation (a.k.a 'NOT') |
Int
Aiken's only number type is an arbitrary sized integer. This means there is no underflow or overflow.
42
14
1337
Literals can also be written with _
as separators to enhance readability:
1_000_000
Aiken also supports writing integer literals in other bases than decimals. Binary, octal, and hexadecimal integers begin with 0b
, 0o
, and 0x
respectively.
0b00001111 == 15
0o17 == 15
0xF == 15
Aiken has several binary arithmetic operators that work with integers.
Operator | Description |
---|---|
+ | Arithmetic sum |
- | Arithmetic difference |
/ | Whole division |
* | Arithmetic multiplication |
% | Remainder by whole division |
Integers are also of course comparable, so they work with a variety of binary logical operators too:
Operator | Description |
---|---|
== | Equality |
> | Greater than |
< | Smaller than |
>= | Greater or equal |
<= | Smaller or equal |
ByteArray
A ByteArray is exactly what it seems, an array of bytes. Aiken supports three notations to declare ByteArray literals.
1 - As an array of bytes
First, as list of integers ranging from 0 to 255 (a.k.a. bytes):
#[10, 255]
#[1, 256] // results in a parse error because 256 is bigger than 1 byte
Syntax rules for literal integers also apply to byte arrays. Thus, the following is a perfectly valid syntax:
#[0xff, 0x42]
2 - As a byte string
Second, as a UTF-8 encoded byte string. This is generally how common text strings are represented under the hood. In Aiken, simply use double-quotes for that:
"foo" == #[0x66, 0x6f, 0x6f] == #[102, 111, 111]
3 - As a hex-encoded byte string
Because it is quite common to manipulate base16-encoded byte strings in a blockchain context (e.g. transaction id, policy id, etc..); Aiken also supports a shorthand syntax for declaring bytearrays as an hexadecimal string.
Behind the scene, Aiken decodes the encoded string for you and stores only the raw bytes as a ByteArray. This is achieved by prefixing a double-quotes byte string with a #
, like so:
#"666f6f" == #[0x66, 0x6f, 0x6f] == #[102, 111, 111] == "foo"
Note how this is different from:
"666f6f" == #[0x36, 0x36, 0x36, 0x66, 0x36, 0x66] == #[54, 54, 54, 102, 54, 54]
List
Lists are ordered collections of values. They're one of the most common data structures in Aiken.
Unlike tuples, all the elements of a List must be of the same type. Attempting to make a list using multiple different types will result in a type error.
[1, 2, 3, 4] // List<Int>
["text", 3, 4] // Type error!
Inserting at the front of a list is very fast, and is the preferred way to add new values.
[1, ..[2, 3]] // [1, 2, 3]
Note that all data structures in Aiken are immutable so prepending to a list does not change the original list. Instead it efficiently creates a new list with the new additional element.
let x = [2, 3]
let y = [1, ..x]
x // [2, 3]
y // [1, 2, 3]
Tuples
Aiken has tuples which can be useful for grouping values. Each element in a tuple can have a different type.
(10, "hello") // Type is (Int, ByteArray)
(1, 4, [0]) // Type is (Int, Int, List<Int>)
Long tuples (i.e. more than 3 elements) are usually discouraged. Indeed, tuples are anonymous constructors, and while they are quick and easy to use, they often impede readability. When types become more complex, one should use records instead (as we'll see later).
Elements of a tuple can be accessed using the dot, followed by the index of the element (ordinal). So for example:
let point = (14, 42, 1337, 0)
let a = point.1st
let b = point.2nd
let c = point.3rd
let d = point.4th
(c, d, b, a) // (1337, 0, 42, 14)
Void
Void
is a type representing the nullary constructor, or put simply, the absence of value. It is denoted Void
as a type and constructor. Fundamentally, if you think in terms of tuples, Void
is a tuple with no element in it.
Void
is useful in certain situations, but because in Aiken everything is a typed expression (there's no "statement"), you'll rarely end up in a situation where you need it.
Data
A Data
is an opaque compound type that can represent any possible user-defined type in Aiken. We'll see more about Data
when we cover custom types. In the meantime, think of Data as a kind of wildcard that can possibly represent any value.
This is useful when you need to use values from different types in an homogeneous structure. Any user-defined type can be cast to a Data
, and you can try converting from a Data to any custom type in a safe manner. Besides, several language builtins only work with Data
as a way to deal with polymorphism.
String
In Aiken text strings can be written as text surrounded by double quotes, prefixed with @
.
@"Hello, Aiken!"
They can span multiple lines.
@"Hello
Aiken!"
Under the hood text strings are UTF-8 (opens in a new tab) encoded binaries and can contain any valid unicode.
@"🌘 アルバイト Aiken 🌒"
Beware the use case for strings is extremely narrow in Aiken and on-chain code. They are only used for tracing, a bit like labels attached to specific execution paths of your program. You can't find them in the interface exposed by your validator, for example. So most of the time, you probably want to use a ByteArray
instead and only resort to String
for debugging.