Reorganize. Add null & error docs
This commit is contained in:
parent
1e63e799ce
commit
893b43e099
@ -1,11 +0,0 @@
|
||||
# Sets
|
||||
|
||||
|
||||
```thp
|
||||
// Set[Int]
|
||||
val ages = Set(30, 31, 33, 35)
|
||||
|
||||
for age in ages {
|
||||
print("{age}")
|
||||
}
|
||||
```
|
@ -8,7 +8,7 @@ Use square brackets as usual.
|
||||
val fruits = ["apple", "banana", "cherry"]
|
||||
val apple = fruits[0]
|
||||
|
||||
print(apple)
|
||||
print(apple) // apple
|
||||
|
||||
|
||||
var numbers = [0, 1, 2, 3]
|
@ -1,4 +1,4 @@
|
||||
# Union types
|
||||
# Enums (Tagged unions)
|
||||
|
||||
## Basic enums
|
||||
|
||||
@ -27,22 +27,22 @@ enum IpAddress
|
||||
val addr_1 = IpAddress::V4("192.168.0.1")
|
||||
|
||||
match addr_1
|
||||
| IpAddress::V4(ip)
|
||||
case IpAddress::V4(ip)
|
||||
{
|
||||
// code..
|
||||
}
|
||||
| IpAddress::V6(ip)
|
||||
case IpAddress::V6(ip)
|
||||
{
|
||||
// more code..
|
||||
}
|
||||
|
||||
// Without the full qualifier
|
||||
match addr_1
|
||||
| ::V4(ip)
|
||||
case ::V4(ip)
|
||||
{
|
||||
// code...
|
||||
}
|
||||
| ::V6(ip)
|
||||
case ::V6(ip)
|
||||
{
|
||||
// even more code...
|
||||
}
|
@ -34,10 +34,18 @@ var Person mary_jane = .{
|
||||
To access the fields of a map we use square braces `[]`.
|
||||
|
||||
```thp
|
||||
mary_jane[age] += 1
|
||||
print(mary_jane[name]) // Mary
|
||||
mary_jane["age"] += 1
|
||||
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
|
||||
|
||||
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
|
||||
// Modify an existing field
|
||||
car[year] = 2015
|
||||
car["year"] = 2015
|
||||
// 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
|
||||
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
|
||||
datatype and returns an Option
|
||||
Instead, we can use the `get` function of the map, which expects a
|
||||
datatype and returns that type as nullable
|
||||
|
||||
```thp
|
||||
// | this function
|
||||
String? car_status = car.get[String]("status")
|
||||
val 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
|
||||
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)
|
||||
```
|
82
md/learn/error-handling/null.md
Normal file
82
md/learn/error-handling/null.md
Normal 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
|
||||
```
|
||||
|
||||
|
||||
|
44
md/learn/error-handling/try.md
Normal file
44
md/learn/error-handling/try.md
Normal 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
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
@ -26,8 +26,8 @@ children:
|
||||
path: loops
|
||||
- name: Match
|
||||
path: match
|
||||
- name: Collections
|
||||
path: collections
|
||||
- name: Data structures
|
||||
path: data-structures
|
||||
children:
|
||||
- name: Tuples
|
||||
path: tuples
|
||||
@ -35,8 +35,8 @@ children:
|
||||
path: arrays
|
||||
- name: Maps
|
||||
path: maps
|
||||
- name: Sets
|
||||
path: sets
|
||||
- name: Enums
|
||||
path: enums
|
||||
- name: Functions
|
||||
path: functions
|
||||
children:
|
||||
@ -48,11 +48,13 @@ children:
|
||||
path: higher-order
|
||||
- name: Lambdas
|
||||
path: lambdas
|
||||
- name: ADTs
|
||||
path: adts
|
||||
- name: Error handling
|
||||
path: error-handling
|
||||
children:
|
||||
- name: Union types
|
||||
path: union-types
|
||||
- name: Nullable types
|
||||
path: "null"
|
||||
- name: Try expressions
|
||||
path: try
|
||||
- name: Classes
|
||||
path: classes
|
||||
children:
|
||||
|
@ -59,7 +59,7 @@
|
||||
</g>
|
||||
</svg>
|
||||
<div class="h-1"></div>
|
||||
<pre style="padding: 0 !important;">
|
||||
<pre style="padding: 0 !important; border: none !important;">
|
||||
<code class="language-thp">
|
||||
fun find_person(Str person_id) -> Result[Str] {
|
||||
val person_id = try person_id.parse[Int]()
|
||||
|
Loading…
Reference in New Issue
Block a user