From 893b43e099670e4837a5fc2559cb0220bf0a1bbf Mon Sep 17 00:00:00 2001 From: Araozu Date: Sat, 16 Mar 2024 09:30:32 -0500 Subject: [PATCH] Reorganize. Add null & error docs --- md/learn/collections/sets.md | 11 --- .../arrays.md | 2 +- .../enums.md} | 10 +-- .../{collections => data-structures}/maps.md | 45 +++++++--- .../tuples.md | 0 md/learn/error-handling/null.md | 82 +++++++++++++++++++ md/learn/error-handling/try.md | 44 ++++++++++ md/learn/index.yaml | 18 ++-- static/index.html | 2 +- 9 files changed, 176 insertions(+), 38 deletions(-) delete mode 100644 md/learn/collections/sets.md rename md/learn/{collections => data-structures}/arrays.md (94%) rename md/learn/{adts/union-types.md => data-structures/enums.md} (80%) rename md/learn/{collections => data-structures}/maps.md (61%) rename md/learn/{collections => data-structures}/tuples.md (100%) create mode 100644 md/learn/error-handling/null.md create mode 100644 md/learn/error-handling/try.md diff --git a/md/learn/collections/sets.md b/md/learn/collections/sets.md deleted file mode 100644 index 357823a..0000000 --- a/md/learn/collections/sets.md +++ /dev/null @@ -1,11 +0,0 @@ -# Sets - - -```thp -// Set[Int] -val ages = Set(30, 31, 33, 35) - -for age in ages { - print("{age}") -} -``` diff --git a/md/learn/collections/arrays.md b/md/learn/data-structures/arrays.md similarity index 94% rename from md/learn/collections/arrays.md rename to md/learn/data-structures/arrays.md index 092796b..cf3e1a8 100644 --- a/md/learn/collections/arrays.md +++ b/md/learn/data-structures/arrays.md @@ -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] diff --git a/md/learn/adts/union-types.md b/md/learn/data-structures/enums.md similarity index 80% rename from md/learn/adts/union-types.md rename to md/learn/data-structures/enums.md index ae653b0..e9a558c 100644 --- a/md/learn/adts/union-types.md +++ b/md/learn/data-structures/enums.md @@ -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... } diff --git a/md/learn/collections/maps.md b/md/learn/data-structures/maps.md similarity index 61% rename from md/learn/collections/maps.md rename to md/learn/data-structures/maps.md index 8ac607d..9d6d9d5 100644 --- a/md/learn/collections/maps.md +++ b/md/learn/data-structures/maps.md @@ -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) +``` diff --git a/md/learn/collections/tuples.md b/md/learn/data-structures/tuples.md similarity index 100% rename from md/learn/collections/tuples.md rename to md/learn/data-structures/tuples.md diff --git a/md/learn/error-handling/null.md b/md/learn/error-handling/null.md new file mode 100644 index 0000000..df0b690 --- /dev/null +++ b/md/learn/error-handling/null.md @@ -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 +``` + + + diff --git a/md/learn/error-handling/try.md b/md/learn/error-handling/try.md new file mode 100644 index 0000000..cd677b9 --- /dev/null +++ b/md/learn/error-handling/try.md @@ -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 +} + +``` + + diff --git a/md/learn/index.yaml b/md/learn/index.yaml index b94addb..16111e7 100644 --- a/md/learn/index.yaml +++ b/md/learn/index.yaml @@ -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: diff --git a/static/index.html b/static/index.html index fbfbb49..d3f14fa 100644 --- a/static/index.html +++ b/static/index.html @@ -59,7 +59,7 @@
-
+            
                 
                     fun find_person(Str person_id) -> Result[Str] {
                         val person_id = try person_id.parse[Int]()