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:
parent
308cbb76ff
commit
0d891daac8
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
```
|
|
@ -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
|
||||
```
|
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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
|
||||
```
|
|
@ -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`.
|
|
@ -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
|
||||
```
|
|
@ -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.
|
|
@ -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
|
||||
)
|
||||
}
|
||||
```
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
}
|
||||
```
|
|
@ -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
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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
|
||||
```
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
```
|
|
@ -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]
|
||||
```
|
|
@ -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 🌒"
|
||||
```
|
|
@ -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
|
||||
)
|
||||
}
|
||||
```
|
|
@ -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))
|
||||
```
|
|
@ -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)
|
||||
```
|
|
@ -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
|
||||
```
|
|
@ -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
|
||||
}
|
||||
```
|
Loading…
Reference in New Issue