Rework the language-tour

To be more progressive in introducing concepts. It also now groups similar concepts together so that they are easier to find and connect with one another.

  I've also added few precisions along the way, as well as corrected a few old syntax (e.g. 'case', generics, tuples..).

  Note that some examples provided in the language tour currently fail to parse / type-check properly.a
This commit is contained in:
KtorZ 2022-12-13 10:55:14 +01:00 committed by Lucas
parent 308cbb76ff
commit 0d891daac8
23 changed files with 524 additions and 580 deletions

View File

@ -3,26 +3,12 @@
- [Introduction](./introduction.md)
- [Getting Started](./getting-started.md)
- [Language Tour](./language-tour.md)
- [Comments](./language-tour/comments.md)
- [String](./language-tour/string.md)
- [Bool](./language-tour/bool.md)
- [Int](./language-tour/int.md)
- [ByteArray](./language-tour/bytearray.md)
- [Data](./language-tour/data.md)
- [Option](./language-tour/option.md)
- [Variables](./language-tour/variables.md)
- [Blocks](./language-tour/blocks.md)
- [List](./language-tour/list.md)
- [Tuple](./language-tour/tuple.md)
- [Custom types](./language-tour/custom-types.md)
- [Type aliases](./language-tour/type-aliases.md)
- [Matching](./language-tour/matching.md)
- [Primitive Types](./language-tour/primitive-types.md)
- [Variables & Constants](./language-tour/variables-and-constants.md)
- [Functions](./language-tour/functions.md)
- [Assert](./language-tour/assert.md)
- [Check](./language-tour/check.md)
- [Todo](./language-tour/todo.md)
- [Custom Types](./language-tour/custom-types.md)
- [Control flow](./language-tour/control-flow.md)
- [Modules](./language-tour/modules.md)
- [Constants](./language-tour/constants.md)
- [Untyped Plutus Core](./uplc.md)
- [Syntax](./uplc/syntax.md)
- [Command-line utilities](./uplc/cli.md)

View File

@ -1,25 +0,0 @@
# Assert
```aiken
type Datum {
n: Int
}
fn do_something(datum: Data) -> Bool {
assert d: Datum = datum
d.n == 0
}
```
Causes the script to fail if the raw `Data` doesn't match the structure of `Datum`.
Primarily for validating input datums / redeemers.
You can also assert patterns.
```aiken
let optional_int = Some(1)
assert Some(x) = optional_int
```

View File

@ -1,18 +0,0 @@
# Blocks
Every block in Aiken is an expression. All expressions in the
block are executed, and the result of the last expression is returned.
```aiken
let value: Bool = {
"Hello"
42 + 12
False
} // => False
```
Expression blocks can be used instead of parenthesis to change the precedence of operations.
```
let celsius = { fahrenheit - 32 } * 5 / 9
```

View File

@ -1,36 +0,0 @@
# Bool
A Bool can be either True or False.
Aiken defines a handful of operators that work with Bools.
```aiken
False && False // => False
False && True // => False
True && False // => False
True && True // => True
False || False // => False
False || True // => True
True || False // => True
True || True // => True
```
These are implemented using the plutus `ifThenElse` builtin.
```aiken
a || b // => if a {True} else {b} -- ifThenElse(a, True, b)
a && b // => if a {b} else {False} -- ifThenElse(a, b, False)
```
An if statement decides on a boolean value.
```aiken
fn negate(b: Bool) -> Bool {
if b {
False
} else {
True
}
}
```

View File

@ -1,20 +0,0 @@
# ByteArray
ByteArrays are exactly what they seem, an array of bytes. In plutus
they are called ByteStrings but we decided ByteArray is a more accurate name.
Aiken supports ByteArray literals.
```aiken
#[10, 255]
#[1, 256] // => results in a parse error because 256 is bigger than 1 byte
```
It's important to mention that variables and patterns are not supported in
ByteArray literals. This is due to how ByteString literals work under the
hood in [UPLC](../uplc.md).
```aiken
let x = 10
#[x, 243] // => not allowed
```

View File

@ -1,19 +0,0 @@
# Check
`check` uses more budget than assert but has stronger guarantees.
# Assert
```aiken
type Datum {
n: Int
}
fn do_something(datum: Data) -> Bool {
check d: Datum = datum
d.n == 0
}
```
Causes the script to fail if the raw `Data` doesn't match the structure of `Datum`.

View File

@ -1,9 +0,0 @@
# Comments
```aiken
// Line comments begin with a double-slash
/// Doc comments begin with a triple-slash
//// Module comments begin with a quadruple-slash
```

View File

@ -1,45 +0,0 @@
# Constants
Aikens's module constants provide a way to use a
certain fixed value in multiple places in a Aiken project.
```aiken
pub const start_year = 2101
pub const end_year = 2111
pub fn is_before(year: Int) -> Bool {
year < start_year
}
pub fn is_during(year: Int) -> Bool {
start_year <= year && year <= end_year
}
```
Like all values in Aiken constants are immutable. They cannot be used as global mutable state.
When a constant is referenced the value is inlined by the compiler, so they can be used in case expression guards.
```aiken
pub const start_year = 2101
pub const end_year = 2111
pub describe(year: Int) -> String {
when year is {
year if year < start_year -> "Before"
year if year > end_year -> "After"
- -> "During"
}
}
```
## Type annotations
Constants can also be given type annotations.
```aiken
pub const name: String = "Aiken"
pub const size: Int = 100
```
These annotations serve as documentation or can be used to provide a more specific type than the compiler would otherwise infer.

View File

@ -1,4 +1,25 @@
# Matching
# Control flow
## Blocks
Every block in Aiken is an expression. All expressions in the block are
executed, and the result of the last expression is returned.
```aiken
let value: Bool = {
"Hello"
42 + 12
False
} // False
```
Expression blocks can be used instead of parenthesis to change the precedence of operations.
```aiken
let celsius = { fahrenheit - 32 } * 5 / 9
```
## Matching
The `when *expr* is` expression is the most common kind of flow control in Aiken code. It
allows us to say "if the data has this shape then do that", which we call
@ -17,19 +38,8 @@ when some_number is {
}
```
Pattern matching on a `Bool` value is discouraged and `if else`
expressions should be use instead.
```aiken
if some_bool {
"It's true!"
else {
"It's not true."
}
```
Aiken's `when *expr* is` is an expression, meaning it returns a value and can be used
anywhere we would use a value. For example, we can name the value of a case
anywhere we would use a value. For example, we can name the value of a when
expression with a `let` binding.
```aiken
@ -49,6 +59,25 @@ let description =
description // => "It's true!"
```
## If-Else
Pattern matching on a `Bool` value is discouraged and `if / else`
expressions should be use instead.
```aiken
let some_bool = True
if some_bool {
"It's true!"
else {
"It's not true."
}
```
Note that, while it may look like an imperative instruction: if this then do
that or else do that, it is in fact one single expression. This means, in
particular, that the return types of both branches have to match.
## Destructuring
A `when *expr* is` expression can be used to destructure values that
@ -64,7 +93,7 @@ when xs is {
```
It's not just the top level data structure that can be pattern matched,
contained values can also be matched. This gives `case` the ability to
contained values can also be matched. This gives `when` the ability to
concisely express flow control that might be verbose without pattern matching.
```aiken
@ -90,21 +119,21 @@ Sometimes when pattern matching we want to assign a name to a value while
specifying its shape at the same time. We can do this using the `as` keyword.
```aiken
case xs {
when xs is {
[[_, ..] as inner_list] -> inner_list
other -> []
_other -> []
}
```
## Checking equality and ordering in patterns
The `if` keyword can be used to add a guard expression to a case clause. Both
The `if` keyword can be used to add a guard expression to a when clause. Both
the patterns have to match and the guard has to evaluate to `True` for the
clause to match. The guard expression can check for equality or ordering for
`Int`.
```aiken
case xs {
when xs is {
[a, b, c] if a == b && b != c -> "ok"
_other -> "ko"
}
@ -112,13 +141,13 @@ case xs {
## Alternative clause patterns
Alternative patterns can be given for a case clause using the `|` operator. If
Alternative patterns can be given for a when clause using the `|` operator. If
any of the patterns match then the clause matches.
Here the first clause will match if the variable `number` holds 2, 4, 6 or 8.
```aiken
case number {
when number is {
2 | 4 | 6 | 8 -> "This is an even number"
1 | 3 | 5 | 7 -> "This is an odd number"
_ -> "I'm not sure"
@ -129,8 +158,56 @@ If the patterns declare variables then the same variables must be declared in
all patterns, and the variables must have the same type in all the patterns.
```aiken
case list {
when list is {
[1, x] | x -> x // Error! Int != List(Int)
_ -> 0
}
```
## Todo
Aiken's `todo` keyword is used to indicate that some code is not yet finished.
It can be useful when designing a module, type checking functions and types but
leaving the implementation of the functions until later.
```aiken
fn not_sure_yet() -> Int {
// The type annotations says this returns an Int, but we don't need
// to implement it yet.
todo
}
fn idk() {
favourite_number() * 2
}
```
When this code is built Aiken will type check and compile the code to ensure it
is valid, and the `todo` will be replaced with code that crashes the program if
that function is run.
A message can be given as a form of documentation. The message will be traced
when the `todo` code is run.
```aiken
fn not_sure_yet() -> Int {
todo("Believe in the you that believes in yourself!")
}
```
When the compiler finds a `todo` it will print a warning, which can be useful
to avoid accidentally forgetting to remove a `todo`.
The warning also includes the expected type of the expression that needs to
replace the `todo`. This can be a useful way of asking the compiler what type
is needed if you are ever unsure.
```aiken
fn foo() {
my_complicated_function(
// What type does this function take again...?
todo
)
}
```

View File

@ -7,7 +7,7 @@ methods.
Custom types are defined with the `type` keyword.
```aiken
pub type Datum {
type Datum {
Datum { signer: ByteArray, count: Int }
}
```
@ -16,8 +16,6 @@ Here we have defined a custom type called `Datum`. Its constructor is called
`Datum` and it has two fields: A `signer` field which is a `ByteArray`, and a
`count` field which is an `Int`.
The `pub` keyword makes this type usable from other [modules](./modules.md).
Once defined the custom type can be used in functions:
```aiken
@ -36,13 +34,13 @@ Custom types in Aiken can be defined with multiple constructors, making them a
way of modeling data that can be one of a few different variants.
We've already seen a custom type with multiple constructors in the Language
Tour - [`Bool`](./bool.md).
Tour - [`Bool`](./primitive-types.md#bool).
Aiken's built-in `Bool` type is defined like this:
```aiken
// A Bool is a value that is either `True` or `False`
pub type Bool {
/// A Bool is a value that is either `True` or `False`
type Bool {
True
False
}
@ -70,6 +68,31 @@ let user2 = LoggedIn { count: 2 }
let visitor = Guest
```
### Option
We define `Option` as a generic type like so:
```
type Option<a> {
None
Some(a)
}
```
Then, functions which fail may safely return an optional value.
```
fn get_head(a: List<a>) -> Option<a {
when a is {
[a, .._] -> Some(a)
[] -> None
}
}
```
The `Option` type is readily available in Aiken; it is part of the default types and values available by default. Don't hesitate to use it!
## Destructuring
When given a custom type record we can pattern match on it to determine which
@ -90,19 +113,17 @@ Custom types can also be destructured with a `let` binding.
type Score {
Points(Int)
}
```
```aiken
let score = Points(50)
let Points(p) = score
let Points(p) = score // This brings a let-binding `p` in scope.
p // => 50
p // 50
```
During destructuring you may also use discards (`_`) or spreads (`..`).
```aiken
pub type Dog {
type Dog {
Dog { name: ByteArray, cuteness: Int, age: Int }
}
@ -117,7 +138,8 @@ spread operator.
let Dog { name: name, cuteness: _, age: _ } = dog
builtin.decode_utf8(name) // "Cashew"
// Other fields ignored by spreading. Field punning is supported.
// Other fields ignored by spreading.
// Field punning is supported. Hence `age` is a shorthand for `age: age`.
let Dog { age, .. } = dog
age // 3
```
@ -127,19 +149,18 @@ age // 3
If a custom type has only one variant and named fields they can be accessed
using `.field_name`.
For example using the `Cat` type defined earlier.
For example using the `Dog` type defined earlier.
```aiken
let dog = Dog { name: #[82, 105, 110], cuteness: 2001 }
builtin.decode_utf8(dog.name) // This returns "Rin"
dog.cuteness // This returns 2001
```
This is actually so common that Aiken has some sugar for defining these
kinds of types.
This is actually so common that Aiken has some syntactic sugar for defining these
kinds of single-constructor types: one can omit the constructor:
```aiken
pub type Dog {
type Dog {
name: ByteArray,
cuteness: Int,
age: Int,
@ -154,80 +175,96 @@ variable.
For example, this `Box` type is a simple record that holds a single value.
```aiken
pub type Box(inner_type) {
type Box<inner_type> {
Box(inner: inner_type)
}
```
The type of the field `inner` is `inner_type`, which is a parameter of the `Box`
type. If it holds an int the box's type is `Box(Int)`, if it holds a string the
box's type is `Box(String)`.
type. If it holds an int the box's type is `Box<Int>`, if it holds a string the
box's type is `Box<String>`.
```aiken
pub fn main() {
let a = Box(420) // type is Box(Int)
let b = Box("That's my ninja way!") // type is Box(String)
fn foo() {
let a = Box(420) // type is Box<Int>
let b = Box("That's my ninja way!") // type is Box<String>
}
```
## Opaque types
At times it may be useful to create a type and make the constructors and
fields private so that users of this type can only use the type through
publically exported functions.
For example we can create a `Counter` type which holds an int which can be
incremented. We don't want the user to alter the int value other than by
incrementing it, so we can make the type opaque to prevent them from being
able to do this.
```aiken
// The type is defined with the opaque keyword
pub opaque type Counter {
Counter(value: Int)
}
pub fn new() {
Counter(0)
}
pub fn increment(counter: Counter) {
Counter(counter.value + 1)
}
```
Because the `Counter` type has been marked as `opaque` it is not possible for
code in other modules to construct or pattern match on counter values or
access the `value` field. Instead other modules have to manipulate the opaque
type using the exported functions from the module, in this case `new` and
`increment`.
## Record updates
Aiken provides a dedicated syntax for updating some of the fields of a custom
type record.
```aiken
pub type Person {
type Person {
name: ByteArray,
shoe_size: Int,
age: Int,
is_happy: Bool,
}
pub fn have_birthday(person) {
fn have_birthday(person) {
// It's this person's birthday, so increment their age and
// make them happy
Person { ..person, age: person.age + 1, is_happy: True }
}
```
Update syntax created a new record with the values of the initial record
with the new values added.
The update syntax created a new record with the values of the initial record.
It replaces the given binding with their new values.
## Plutus
## Type aliases
At runtime custom types become instances of `PlutusData`.
A type alias lets you create a name which is identical to
another type, without any additional information.
In Aiken's type system `Data` matches with any user-defined type
except for most of the types included in the prelude module.
```aiken
type MyNumber = Integer
```
They are most useful for simplifying type signatures.
```aiken
type Person = (String, Integer)
fn create_person(name: String, age: Integer) -> Person {
#(name, age)
}
```
## Data
At runtime custom types become an opaque Plutus' Data. In Aiken's type system
`Data` matches with any user-defined type (but with none of the primitive
types).
Thus, it's also possible to cast any `Data` to a [custom
type](./custom-types.md), and vice versa.
```aiken
fn to_datum(datum: Data) -> Datum {
let d: Datum = datum
d
}
```
Note that this conversion will fail if the given `Data` isn't actually a valid
representation of the target type. The primary use-case here is for
instantiating script contexts, datums and redeemers provided to scripts in an
opaque fashion.
It's also useful for interacting with builtins that operate on raw `Data`. In
this case, the conversation happens implicitly. Simply expect any function that
accept `Data` to automatically work on any custom type.
```aiken
type Datum {
count: Int,
}
let datum = Datum { count: 1 }
// fn(Data) -> ByteArray
builtin.serialize_data(datum) // some bytearray
```

View File

@ -1,29 +0,0 @@
# Data
`Data` is the equivalent to `BuiltinData` in plutus. In Aiken,
it's a way to generically represent [custom types](./custom-types.md). In a way, it's
almost like `any` in TypeScript but restricted to user-defined [custom types](./custom-types.md).
As a type it won't match Int, Bool, ByteArray, and String.
It's useful for interacting with builtins that operate on `PlutusData'.
```aiken
type Datum {
count: Int,
}
let datum = Datum { count: 1 }
// fn(Data) -> ByteArray
builtin.serialize_data(datum) // some bytearray
```
It's also possible to cast `Data` to a [custom type](./custom-types.md).
```aiken
pub fn do_cast(datum: Data) -> Datum {
let d: Datum = datum
d
}
```

View File

@ -2,32 +2,32 @@
## Named functions
Named functions in Aiken are defined using the `pub fn` keywords.
Named functions in Aiken are defined using the `fn` keyword. Functions can have (typed) arguments, and always have a return type. Because in Aiken, pretty much everything is an expression, functions do not have an explicit _return_ keyword. Instead, they implicitly return whatever they evaluate to.
```aiken
pub fn add(x: Int, y: Int) -> Int {
fn add(x: Int, y: Int) -> Int {
x + y
}
pub fn multiply(x: Int, y: Int) -> Int {
fn multiply(x: Int, y: Int) -> Int {
x * y
}
```
Functions in Aiken are first class values and so can be assigned to variables,
passed to functions, or anything else you might do with any other data type.
Functions are first class values and so can be assigned to variables, passed to
other functions, or anything else you might do with any other data type.
```aiken
/// This function takes a function as an argument
pub fn twice(f: fn(t) -> t, x: t) -> t {
fn twice(f: fn(t) -> t, x: t) -> t {
f(f(x))
}
pub fn add_one(x: Int) -> Int {
fn add_one(x: Int) -> Int {
x + 1
}
pub fn add_two(x: Int) -> Int {
fn add_two(x: Int) -> Int {
twice(add_one, x)
}
```
@ -52,7 +52,7 @@ string
|> string_builder.to_string
```
Each line of this expression applies the function to the result of the previous line. This works easily because each of these functions take only one argument. Syntax is available to substitute specific arguments of functions that take more than one argument; for more, look below in the section "Function capturing".
Each line of this expression applies the function to the result of the previous line. This works easily because each of these functions takes only one argument. Syntax is available to substitute specific arguments of functions that take more than one argument; for more, look below in the section "Function capturing".
## Type annotations
@ -83,7 +83,7 @@ containing two of the value that was passed in. This can be expressed in Aiken
like this:
```aiken
fn list_of_two(my_value: a) -> List(a) {
fn list_of_two(my_value: a) -> List<a> {
[my_value, my_value]
}
```
@ -94,8 +94,8 @@ You can use any number of different type variables in the same function. This
function declares type variables `a` and `b`.
```aiken
fn multi_result(x: a, y: b, condition: Bool) -> Result(a, b) {
case condition {
fn multi_result(x: a, y: b, condition: Bool) -> Result<a, b> {
when condition is {
True -> Ok(x)
False -> Error(y)
}
@ -117,7 +117,7 @@ arguments are given an external label in addition to their internal name.
Take this function that replaces sections of a string:
```aiken
pub fn replace(string: String, pattern: String, replacement: String) {
fn replace(string: String, pattern: String, replacement: String) {
// ...
}
```
@ -125,7 +125,7 @@ pub fn replace(string: String, pattern: String, replacement: String) {
It can be given labels like so.
```aiken
pub fn replace(
fn replace(
in string: String,
each pattern: String,
with replacement: String,
@ -155,7 +155,7 @@ and clear in intent.
Anonymous functions can be defined with a similar syntax.
```aiken
pub fn run() {
fn run() {
let add = fn(x, y) { x + y }
add(1, 2)
@ -169,11 +169,11 @@ argument and call another function. The `_` is used to indicate where the
argument should be passed.
```aiken
pub fn add(x, y) {
fn add(x, y) {
x + y
}
pub fn run() {
fn run() {
let add_one = add(1, _)
add_one(2)
@ -184,11 +184,11 @@ The function capture syntax is often used with the pipe operator to create
a series of transformations on some data.
```aiken
pub fn add(x: Int , y: Int ) -> Int {
fn add(x: Int , y: Int ) -> Int {
x + y
}
pub fn run() {
fn run() {
// This is the same as add(add(add(1, 3), 6), 9)
1
|> add(_, 3)
@ -200,7 +200,7 @@ pub fn run() {
In fact, this usage is so common that there is a special shorthand for it.
```aiken
pub fn run() {
fn run() {
// This is the same as the example above
1
|> add(3)
@ -222,9 +222,9 @@ documentation comment `///` per line. Markdown is supported and this text
will be included with the module's entry in generated HTML documentation.
```aiken
/// Does nothing, returns `Nil`.
/// Always true.
///
fn returns_nil(a) -> Nil {
Nil
fn always_true(_a) -> Bool {
True
}
```

View File

@ -1,39 +0,0 @@
# Int
Aiken's only number type is an arbitrary size integer.
This means there is no underflow or overflow.
Binary, octal, and hexadecimal integers begin with 0b, 0o, and 0x respectively.
```aiken
1
2
-3
4001
0b00001111
0o17
0xF
```
Aiken has several operators that work with Int.
```aiken
// A convenient helper function to get the number 7.
1 + 1 // => 2
5 - 1 // => 4
5 / 2 // => 2
3 * 3 // => 9
5 % 2 // => 1
2 > 1 // => True
2 < 1 // => False
2 >= 1 // => True
2 <= 1 // => False
```
Underscores can be added to Ints for clarity.
```
1_000_000
```

View File

@ -1,41 +0,0 @@
# List
Lists are ordered collections of values. They're one of the most common data structures in Aiken.
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.
```aiken
[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.
```aiken
[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]
```
Accessing head, tail, or preceding elements can be done by pattern matching.
```aiken
// this function checks if a list has a sequence of 1 then 2 contained within it.
fn list_stuff(a: List(Int)){
when a is {
[1, 2, ..tail] -> True
[] -> False
_ -> list_stuff([2, ..tail])
}
}
```

View File

@ -28,10 +28,15 @@ filename `lib/straw_hats/sunny.ak`. Typically all the modules for one
project would live within a directory with the name of the project, such as
`straw_hats` in this example.
The `pub` keyword makes this type usable from other modules.
For the functions `count_down` and `blast_off` we have omitted the `pub`
keyword, so these functions are _private_ module functions. They can only be
called by other functions within the same module.
Functions, type-aliases and constants can all be exported from a module using
the `pub` keyword.
## Import
To use functions or types from another module we need to import them using the
@ -75,7 +80,7 @@ Values and types can also be imported in an unqualified fashion.
```aken
use animal/dog.{Dog, stroke}
pub fn main() {
pub fn foo() {
let puppy = Dog { name: "Zeus" }
stroke(puppy)
}
@ -85,25 +90,57 @@ This may be useful for values that are used frequently in a module, but
generally qualified imports are preferred as it makes it clearer where the
value is defined.
## Opaque types
At times it may be useful to create a type and make the constructors and
fields private so that users of this type can only use the type through
publically exported functions.
For example we can create a `Counter` type which holds an int which can be
incremented. We don't want the user to alter the int value other than by
incrementing it, so we can make the type opaque to prevent them from being
able to do this.
```aiken
// The type is defined with the opaque keyword
pub opaque type Counter {
Counter(value: Int)
}
pub fn new() {
Counter(0)
}
pub fn increment(counter: Counter) {
Counter(counter.value + 1)
}
```
Because the `Counter` type has been marked as `opaque` it is not possible for
code in other modules to construct or pattern match on counter values or
access the `value` field. Instead other modules have to manipulate the opaque
type using the exported functions from the module, in this case `new` and
`increment`.
## The prelude module
There are two modules that are built into the language, the first is the `aiken` prelude
module. By default its types and values are automatically imported into
every module you write, but you can still chose to import it the regular way.
This may be useful if you have created a type or value with the same name as
an item from the prelude.
There are two modules that are built into the language, the first is the
`aiken` prelude module. By default its types and values are automatically
imported into every module you write, but you can still chose to import it the
regular way. This may be useful if you have created a type or value with the
same name as an item from the prelude.
```aiken
use aiken
/// This definition locally overrides the `Result` type
/// This definition locally overrides the `Option` type
/// and the `Some` constructor.
pub type Option {
Some
}
/// The original `Option` and `Some` can still be used
pub fn go() -> aiken.Option(Int) {
pub fn go() -> aiken.Option<Int> {
aiken.Some(1)
}
```
@ -113,9 +150,9 @@ The prelude module contains these types:
- `ByteArray`
- `Bool`
- `Int`
- `List(element)`
- `List<element>`
- `Nil`
- `Option(value)`
- `Option<value>`
- `String`
- `Data`
@ -129,14 +166,13 @@ And these values:
## The builtin module
The second module that comes with the language is for exposing
useful builtin functions from plutus core. Most underlying
platform functions are available here using a snake case name.
Much of Aiken's syntax ends up compiling to combinations of certain bultins
but many aren't "exposed" through the syntax and need to be used directly.
The standard library wraps these in a more Aiken friendly interface so
you'll probably never need to use these directly unless you're making your
own standard library.
The second module that comes with the language is for exposing useful builtin
functions from Plutus core. Most underlying platform functions are available
here using a snake case name. Much of Aiken's syntax ends up compiling to
combinations of certain bultins but many aren't "exposed" through the syntax
and need to be used directly. The standard library wraps these in a more
Aiken-friendly interface so you'll probably never need to use these directly
unless you're making your own standard library.
```aiken
use aiken/builtin
@ -144,10 +180,15 @@ use aiken/builtin
fn eq(a, b) {
builtin.equals_integer(a, b)
}
// is implicitly used when doing:
a == b
```
## Documentation
You may add user facing documentation at the head of modules with a module
documentation comment `////` per line. Markdown is supported and this text
will be included with the module's entry in generated HTML documentation.
documentation comment `////` (quadruple slash!) per line. Markdown is supported
and this text block will be included with the module's entry in generated HTML
documentation.

View File

@ -1,21 +0,0 @@
# Option
We define option as a generic type like so
```
pub type Option(a) {
None
Some(a)
}
```
Then, functions which fail may safely return an optional value.
```
pub fn get_head(a: List(a)) -> Option(a) {
when a is {
[a, .._] -> Some(a)
[] -> None
}
}
```

View File

@ -0,0 +1,175 @@
# Primitive Types
Aiken has 5 primitive types that are built in the language and can be typed as literals: booleans, integers, strings, byte arrays and data. 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.
> **Note**
>
> 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') |
| <code>\|\|</code> | 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.
```aiken
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.
```aiken
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 |
## String
In Aiken Strings can be written as text surrounded by double quotes.
```aiken
"Hello, Aiken!"
```
They can span multiple lines.
```aiken
"Hello
Aiken!"
```
Under the hood text strings are [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded binaries
and can contain any valid unicode.
```aiken
"🌘 アルバイト Aiken 🌒"
```
## ByteArray
A _ByteArray_ is exactly what it seems, an array of bytes.
Aiken supports byte arrays literals, written as lists of integers ranging from 0 to 255 (a.k.a _bytes_):
```aiken
#[10, 255]
#[1, 256] // results in a parse error because 256 is bigger than 1 byte
```
It's important to mention that variables and patterns are not supported in
byte array literals due to how Untyped Plutus Core works behind the scene.
```aiken
let x = 10
#[x, 243] // not allowed
```
However, syntax rules for literal integers also apply to byte arrays. Thus, the following is a perfectly valid syntax:
```aiken
#[0xFF, 0x42]
```
## Data
A _Data_ is an opaque compound type that can represent any possible user-defined type in Aiken. We'll see later how Aiken lets you compose your own types from the primitives we just presented. 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.
We'll see more about _Data_ when we cover custom types.
## Tuples
Aiken has tuples which can be useful for grouping values. Each element in a tuple can have a different type.
```aiken
(10, "hello") // Type is (Int, String)
(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 (0-based indexing). So for example:
```aiken
let point = (14, 42)
let x = point.0
let y = point.1
(y, x) // (42, 14)
```
## 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.
```aiken
[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.
```aiken
[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]
```

View File

@ -1,21 +0,0 @@
# String
In Aiken Strings can be written as text surrounded by double quotes.
```aiken
"Hello, Aiken!"
```
They can span multiple lines.
```aiken
"Hello
Aiken!"
```
Under the hood Strings are [UTF-8](https://en.wikipedia.org/wiki/UTF-8) encoded binaries
and can contain any valid unicode.
```aiken
"🌘 アルバイト Aiken 🌒"
```

View File

@ -1,47 +0,0 @@
# Todo
Aiken's `todo` keyword is used to indicate that some code is not yet finished.
It can be useful when designing a module, type checking functions and types
but leaving the implementation of the functions until later.
```aiken
fn not_sure_yet() -> Int {
// The type annotations says this returns an Int, but we don't need
// to implement it yet.
todo
}
pub fn idk() {
favourite_number() * 2
}
```
When this code is built Aiken will type check and compile the code to ensure
it is valid, and the `todo` will be replaced with code that crashes the
program if that function is run.
A message can be given as a form of documentation. The message will be traced when
the `todo` code is run.
```aiken
fn not_sure_yet() -> Int {
todo("Believe in the you that believes in yourself!")
}
```
When the compiler finds a `todo` it will print a warning, which can be useful
to avoid accidentally forgetting to remove a `todo`.
The warning also includes the expected type of the expression that needs to
replace the `todo`. This can be a useful way of asking the compiler what type
is needed if you are ever unsure.
```aiken
fn main() {
my_complicated_function(
// What type does this function take again...?
todo
)
}
```

View File

@ -1,8 +0,0 @@
# Tuple
Aiken has tuples which can be useful for grouping values.
```aiken
#(10, "hello") // Type is #(Int, String)
#(1, 4, [0]) // Type is #(Int, Int, List(Int))
```

View File

@ -1,24 +0,0 @@
# Type aliases
A type alias lets you create a name which is identical to
another type, without any additional information.
```aiken
type MyNumber = Integer
```
They are most useful for simplifying type signatures.
```aiken
type Person = #(String, Integer)
fn create_person(name: String, age: Integer) -> Person {
#(name, age)
}
```
If you want the type alias to be accessible from a module, you can define it as public.
```
pub type MyVector3 = #(Integer, Integer, Integer)
```

View File

@ -0,0 +1,61 @@
# Variables & Constants
## Variables
Aiken has let-bindings for variables. A value can be given a name using the keyword `let`.
Names can be reused by later let-bindings.
Values assigned to let-bindings are immutable, however new bindings can shadow
previous bindings.
```aiken
let x = 1
let y = x
let x = 2
y + x == 3
```
## Constants
Let-bindings aren't allowed in a top-level Aiken module. Yet, Aiken provides
module constants as a way to use certain fixed values in multiple places of a
Aiken project.
```aiken
const start_year = 2101
const end_year = 2111
```
Like all values in Aiken, constants are immutable. They cannot be used as global mutable state. When a constant is referenced, its value is inlined by the compiler so they can be used in any place where you'd have written a literal in the first place (e.g. when-expression guards, if clauses ...). We'll see some example of that when dealing with control flows.
## Type annotations
Variables and constants can be given type annotations. These annotations serve as documentation or can be used to provide a more specific type than the compiler would otherwise infer.
```aiken
const name: String = "Aiken"
const size: Int = 100
let result: Bool = 14 > 42
```
## assert
Sometimes, it is useful to fail the exit and fail the execution of a program
early on. This is where the `assert` keywords comes in handy. `assert` causes
the script to fail if it fails to bind a given expression.
It is particularly useful when combined with custom types that can have
multiple constructors, and that are expected to have a very specific shape. It
is primarily used for validating datums and redeemers -- which can really be
anything.
Having ways to enforce that some inputs have the requested shape is thus
often necessary. For example, let's consider we have a custom type `MyDatum`
that we want to turn some opaque `Data` into. We can do the following:
```aiken
// some_data : Data
assert my_datum : MyDatum = some_data
```

View File

@ -1,31 +0,0 @@
# Variables
Aiken has let bindings for variables. A value can be given a name using let.
Names can be reused by later let bindings, but the values contained are immutable.
```aiken
let x = 3
let v = x
let x = 7
x // => 2
y // => 1
```
Function variables with types
```
fn add(a: Int, b: Int) -> Int {
a + b
}
```
Let bindings
```
fn something() {
let a = 3
let b = 5
a + b
}
```