Reorganize. Add null & error docs

master
Araozu 2024-03-16 09:30:32 -05:00
parent 1e63e799ce
commit 893b43e099
9 changed files with 176 additions and 38 deletions

View File

@ -1,11 +0,0 @@
# Sets
```thp
// Set[Int]
val ages = Set(30, 31, 33, 35)
for age in ages {
print("{age}")
}
```

View File

@ -8,7 +8,7 @@ Use square brackets as usual.
val fruits = ["apple", "banana", "cherry"] val fruits = ["apple", "banana", "cherry"]
val apple = fruits[0] val apple = fruits[0]
print(apple) print(apple) // apple
var numbers = [0, 1, 2, 3] var numbers = [0, 1, 2, 3]

View File

@ -1,4 +1,4 @@
# Union types # Enums (Tagged unions)
## Basic enums ## Basic enums
@ -27,22 +27,22 @@ enum IpAddress
val addr_1 = IpAddress::V4("192.168.0.1") val addr_1 = IpAddress::V4("192.168.0.1")
match addr_1 match addr_1
| IpAddress::V4(ip) case IpAddress::V4(ip)
{ {
// code.. // code..
} }
| IpAddress::V6(ip) case IpAddress::V6(ip)
{ {
// more code.. // more code..
} }
// Without the full qualifier // Without the full qualifier
match addr_1 match addr_1
| ::V4(ip) case ::V4(ip)
{ {
// code... // code...
} }
| ::V6(ip) case ::V6(ip)
{ {
// even more code... // even more code...
} }

View File

@ -34,10 +34,18 @@ var Person mary_jane = .{
To access the fields of a map we use square braces `[]`. To access the fields of a map we use square braces `[]`.
```thp ```thp
mary_jane[age] += 1 mary_jane["age"] += 1
print(mary_jane[name]) // Mary print(mary_jane["name"]) // Mary
``` ```
Or dot access `.` if the field's name is a valid identifier.
```thp
mary_jane.age += 1
print(mary_jane.name)
```
## Anonymous maps ## Anonymous maps
An anonymous map allows us to store and retrieve any key of any datatype. An anonymous map allows us to store and retrieve any key of any datatype.
@ -69,26 +77,39 @@ We can freely assign fields to an anonymous map:
```thp ```thp
// Modify an existing field // Modify an existing field
car[year] = 2015 car["year"] = 2015
// Create a new field // Create a new field
car[status] = "used" car["status"] = "used"
``` ```
However, if we try to access a field of an anonymous map we'll get an error. However, if we try to access a field of an anonymous map we'll get
a nullable type, and we must annotate it.
```thp ```thp
print(car[status]) // Error: Can't access a field of an anonymous map // This is ok, we are declaring what datatype we expect
String? car_status = car["status"]
// This won't work, the compiler doesn't know what datatype to use
var car_status = car["status"]
``` ```
Instead, we should use the `get` function of the map, which expects a Instead, we can use the `get` function of the map, which expects a
datatype and returns an Option datatype and returns that type as nullable
```thp ```thp
// | this function val car_status = car.get[String]("status")
String? car_status = car.get[String]("status")
``` ```
The `get` function will check that a key `"status"` exists in the map, Both ways to get a value will check that the key exists in the map,
and that it has the correct datatype. If either the key doesn't exist and that it has the correct datatype. If either the key doesn't exist
or it has a different datatype, it will return `None`. or it has a different datatype, it will return `null`.
We can also use dynamic keys, following the same rules:
```thp
val generated_value = "key"
String? v = map[generated_value]
// or
val v = map[String](generated_value)
```

View File

@ -0,0 +1,82 @@
# Nullable types
All datatypes in THP disallow the usage of `null` by default.
To represent `null` we must use nullable types, represented
by the question mark `?` character.
For instance, a POST request may have a `username` parameter,
or it may not. This can be represented with an `String?`.
```thp
String? new_username = POST::get("username")
```
When we have a `Type?` we cannot use it directly. We must first
check if the value is null, and then use it.
```thp
if new_username != null {
// Here `new_username` is automatically casted to String
}
```
We must check explicitly that the value is not null. Doing
`if new_username {}` alone is not allowed.
## Usage
To create a nullable type we must explicitly annotate the type.
```thp
val favorite_color = null // Error, we must define the type
String? favorite_color = null // Ok
```
Other examples:
```thp
fun get_first(Array[String?] values) -> String? {}
val result = get_first([])
```
## Optional chaining
If you have a `Type?` and you wish to access a field of `Type` if it exists,
you can use the optional chaining operator.
```thp
Person? person = ...
val name = person?.name
```
- If `person` is null, `person?.name` will return `null`
- If `person` is not null, `person?.name` will return `name`
## Elvis operator
The Elvis operator `??` is used to give a default value in case a `null` is found.
```thp
// This is a function that may return a Int
fun get_score() -> Int? {...}
val test_score = get_score() ?? 0
```
For the above code:
- If `get_score()` is not null, `??` will return `get_score()`
- If `get_score()` *is* null, `??` will return `0`
You can use the Elvis operator to return early
```thp
val username = get_username() ?? return
```

View File

@ -0,0 +1,44 @@
# Try expressions
```thp
fun get_value() -> Result[Int, String] { ... }
// treating errors as normal enums
val result = match get_value()
case Ok(result) { result }
case Err(error) { return error }
// get the value if Ok, otherwise re-throw
val result = try get_value()
// get the value if Ok, return a new value otherwise
val result = try get_value() return Err("new error")
// get the value if Ok, assign a new value otherwise
Int result = try get_value() else 20
// get the value if Ok, otherwise run block with the error value
val result = try get_value()
with error
{
// handle error
}
fun throws_exception() -> Result[Int, MyException|MyOtherException] { ... }
val result = try throws_exception() catch
case MyException(e)
{
// deal with MyException
}
case MyOtherException(e)
{
// deal with MyOtherException
}
```

View File

@ -26,8 +26,8 @@ children:
path: loops path: loops
- name: Match - name: Match
path: match path: match
- name: Collections - name: Data structures
path: collections path: data-structures
children: children:
- name: Tuples - name: Tuples
path: tuples path: tuples
@ -35,8 +35,8 @@ children:
path: arrays path: arrays
- name: Maps - name: Maps
path: maps path: maps
- name: Sets - name: Enums
path: sets path: enums
- name: Functions - name: Functions
path: functions path: functions
children: children:
@ -48,11 +48,13 @@ children:
path: higher-order path: higher-order
- name: Lambdas - name: Lambdas
path: lambdas path: lambdas
- name: ADTs - name: Error handling
path: adts path: error-handling
children: children:
- name: Union types - name: Nullable types
path: union-types path: "null"
- name: Try expressions
path: try
- name: Classes - name: Classes
path: classes path: classes
children: children:

View File

@ -59,7 +59,7 @@
</g> </g>
</svg> </svg>
<div class="h-1"></div> <div class="h-1"></div>
<pre style="padding: 0 !important;"> <pre style="padding: 0 !important; border: none !important;">
<code class="language-thp"> <code class="language-thp">
fun find_person(Str person_id) -> Result[Str] { fun find_person(Str person_id) -> Result[Str] {
val person_id = try person_id.parse[Int]() val person_id = try person_id.parse[Int]()