Change syntax for null/errors

This commit is contained in:
Araozu 2024-08-06 17:52:55 -05:00
parent 8b3f4a88c1
commit a476fffe2c
3 changed files with 62 additions and 27 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -11,19 +11,29 @@ To represent `null` we must use nullable types, represented
by the question mark `?` character. by the question mark `?` character.
For instance, a POST request may have a `username` parameter, For instance, a POST request may have a `username` parameter,
or it may not. This can be represented with an `String?`. or it may not. This can be represented with an `?String`.
<Code thpcode={` <Code thpcode={`
String? new_username = POST::get("username") ?String new_username = POST::get("username")
`} /> `} />
When we have a `Type?` we cannot use it directly. We must first When we have a `?Type` we cannot use it directly. We must first
check if the value is null, and then use it. check if the value is null, and then use it.
The syntax `?` returns `true` if the value is not null.
<Code thpcode={` <Code thpcode={`
if new_username != null { if new_username?
{
// Here \`new_username\` is automatically casted to String // Here \`new_username\` is automatically casted to String
} }
// you can also manually check for null
if new_username == null
{
// This is the same as above
}
`} /> `} />
We must check explicitly that the value is not null. Doing We must check explicitly that the value is not null. Doing
@ -36,24 +46,24 @@ To create a nullable type we must explicitly annotate the type.
<Code thpcode={` <Code thpcode={`
val favorite_color = null // Error, we must define the type val favorite_color = null // Error, we must define the type
String? favorite_color = null // Ok ?String favorite_color = null // Ok
`} /> `} />
Other examples: Other examples:
<Code thpcode={` <Code thpcode={`
fun get_first(Array[String?] values) -> String? {} fun get_first(Array[?String] values) -> ?String {}
val result = get_first([]) val result = get_first([])
`} /> `} />
## Optional chaining ## Optional chaining
If you have a `Type?` and you wish to access a field of `Type` if it exists, If you have a `?Type` and you wish to access a field of `Type` if it exists,
you can use the optional chaining operator. you can use the optional chaining operator `?.`.
<Code thpcode={` <Code thpcode={`
Person? person = ... ?Person person = ...
val name = person?.name val name = person?.name
`} /> `} />
@ -62,13 +72,41 @@ val name = person?.name
- If `person` is not null, `person?.name` will return `name` - If `person` is not null, `person?.name` will return `name`
## Null unboxing
The `!!` operator transforms a `?Type` into `Type`.
If you are sure that a value cannot be `null`, you can force the
compiler to treat it as a regular value with the `!!` operator.
Note the two exclamation marks.
<Code thpcode={`
?String lastname = find_lastname()
// Tell the compiler trust me,
// I know this is not null
String s = lastname!!
`} />
You can use it to chain access:
<Code thpcode={`
val children_lastname = person!!.child!!.lastname
`} />
However, if at runtime you use `!!` on a null value,
the null value will be returned and your program will
blow up later. So make sure to use this operator
only when you are sure a value cannot be null.
## Elvis operator ## Elvis operator
The Elvis operator `??` is used to give a default value in case a `null` is found. The Elvis operator `??` is used to give a default value in case a `null` is found.
<Code thpcode={` <Code thpcode={`
// This is a function that may return a Int // This is a function that may return a Int
fun get_score() -> Int? {...} fun get_score() -> ?Int {...}
val test_score = get_score() ?? 0 val test_score = get_score() ?? 0
`} /> `} />

View File

@ -12,14 +12,14 @@ and handled.
## Declare that a function returns an exception ## Declare that a function returns an exception
To declare a possible error return value the `Result` enum Possible errors have their own syntax: `Error!Type`.
is used. This means: This may be an `Error`, or a `Type`.
For example, a function that returned a `DivisionByZero` For example, a function that returned a `DivisionByZero`
may be written like this: may be written like this:
<Code thpcode={` <Code thpcode={`
fun invert(Int number) -> Int!DivisionByZero fun invert(Int number) -> DivisionByZero!Int
{ {
if number == 0 if number == 0
{ {
@ -30,25 +30,22 @@ fun invert(Int number) -> Int!DivisionByZero
} }
`} /> `} />
In the previous segment, `Int!DivisionByZero` denotates In the previous segment, `DivisionByZero!Int` denotates
that the function may return either an `Int` or an `DivisionByZero`. that the function may return either a `DivisionByZero` error
or an `Int`.
We then can return the error or success value; There is no `throw` keyword, errors are just returned.
### Multiple error returns ### Multiple error returns
TODO: fix? TODO: properly define syntax, how this interacts with type unions.
Multiple errors are chained with `!`. The last one is always
the success value.
If there are multiple error types that the function can return,
you can use the `|` operator:
<Code thpcode={` <Code thpcode={`
type Exceptions = Exception1 | Exception2 | Exception3 fun sample() -> Error1!Error2!Error3!Int
fun sample() -> Int!Exceptions
{ /* ... */} { /* ... */}
`} /> `} />
@ -68,13 +65,13 @@ Use a naked `try` when you want to rethrow an error, if there is any.
<InteractiveCode <InteractiveCode
code={` code={`
fun dangerous() -> Int!Exception fun dangerous() -> Exception!Int
{ // May throw randomly { // May throw randomly
return if Math.random() < 0.5 { 50 } return if Math.random() < 0.5 { 50 }
else { Exception("Unlucky") } else { Exception("Unlucky") }
} }
fun run() -> !Exception fun run() -> Exception!
{ // If \`dangerous()\` throws, the function exits with the same error. { // If \`dangerous()\` throws, the function exits with the same error.
// Otherwise, continues // Otherwise, continues
val result = try dangerous() val result = try dangerous()
@ -156,7 +153,7 @@ Try/else will assign a new value if an expression fails.
<InteractiveCode <InteractiveCode
code={` code={`
fun run(Int!Exception possible_value) fun run(Exception!Int possible_value)
{ {
val mid = try possible_value else 666 val mid = try possible_value else 666