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 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]
|
@ -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...
|
||||||
}
|
}
|
@ -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)
|
||||||
|
```
|
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
|
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:
|
||||||
|
@ -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]()
|
||||||
|
Loading…
Reference in New Issue
Block a user