diff --git a/ci_scripts/all_tests.sh b/ci_scripts/all_tests.sh
index c2a1cbb..2a61dac 100755
--- a/ci_scripts/all_tests.sh
+++ b/ci_scripts/all_tests.sh
@@ -98,7 +98,7 @@ $ROC test ./examples/CustomInspect/OpaqueTypes.roc
if [[ "$(uname)" != "Darwin" ]]; then
$ROC build --lib ./examples/GoPlatform/main.roc --output examples/GoPlatform/platform/libapp.so
go build -C examples/GoPlatform/platform -buildmode=pie -o dynhost
-
+
$ROC preprocess-host ./examples/GoPlatform/platform/dynhost ./examples/GoPlatform/platform/main.roc ./examples/GoPlatform/platform/libapp.so
$ROC build ./examples/GoPlatform/main.roc
@@ -119,3 +119,5 @@ if [[ "$(uname)" != "Darwin" ]]; then
$ROC build ./examples/DotNetPlatform/main.roc --lib --output ./examples/DotNetPlatform/platform/interop
expect ci_scripts/expect_scripts/DotNetPlatform.exp
fi
+
+echo "All tests passed!"
diff --git a/ci_scripts/expect_scripts/MultipleRocFiles.exp b/ci_scripts/expect_scripts/MultipleRocFiles.exp
index c1d364a..d3dbceb 100644
--- a/ci_scripts/expect_scripts/MultipleRocFiles.exp
+++ b/ci_scripts/expect_scripts/MultipleRocFiles.exp
@@ -7,7 +7,7 @@ set timeout 7
spawn ./examples/MultipleRocFiles/main
-expect "Hello World from interface!\r\n" {
+expect "Hello World from module!\r\n" {
expect eof
exit 0
}
diff --git a/examples/Arithmetic/main.roc b/examples/Arithmetic/main.roc
index 1eb5679..f45c83e 100644
--- a/examples/Arithmetic/main.roc
+++ b/examples/Arithmetic/main.roc
@@ -1,13 +1,13 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
-import pf.Stdout
-import pf.Arg exposing [Arg]
+import cli.Stdout
+import cli.Arg exposing [Arg]
main! : List Arg.Arg => Result {} _
-main! = \raw_args ->
+main! = |raw_args|
args : { a : I32, b : I32 }
- args = try read_args raw_args
+ args = read_args(raw_args)?
result =
[
@@ -16,44 +16,51 @@ main! = \raw_args ->
("product", args.a * args.b),
("integer quotient", args.a // args.b),
("remainder", args.a % args.b),
- ("exponentiation", Num.powInt args.a args.b),
+ ("exponentiation", Num.pow_int(args.a, args.b)),
]
- |> List.map \(operation, answer) ->
- answer_str = Num.toStr answer
+ |> List.map(
+ |(operation, answer)| "${operation}: ${Num.to_str(answer)}",
+ )
+ |> Str.join_with("\n")
- "$(operation): $(answer_str)"
- |> Str.joinWith "\n"
-
- Stdout.line! result
+ Stdout.line!(result)
## Reads two command-line arguments, attempts to parse them as `I32` numbers,
-## and returns a task containing a record with two fields, `a` and `b`, holding
-## the parsed `I32` values.
+## and returns a [`Result`](https://www.roc-lang.org/builtins/Result).
+##
+## On success, the [`Result`](https://www.roc-lang.org/builtins/Result)
+## will contain a record with two fields, `a` and `b`, holding the parsed `I32` values.
##
-## If the arguments are missing, if there's an issue with parsing the arguments
-## as `I32` numbers, or if the parsed numbers are outside the expected range
-## (-1000 to 1000), the function will return a task that fails with an
-## error `InvalidArg` or `InvalidNumStr`.
+## This will fail if an argument is missing, if there's an issue with parsing
+## the arguments as `I32` numbers, or if the parsed numbers are outside the
+## expected range (-1000 to 1000). Then the [`Result`](https://www.roc-lang.org/builtins/Result) will contain
+## an `Exit I32 Str` error.
read_args : List Arg -> Result { a : I32, b : I32 } [Exit I32 Str]
-read_args = \raw_args ->
+read_args = |raw_args|
+ arg_range_min = -1000
+ arg_range_max = 1000
+ expected_nr_of_args = 2 + 1 # +1 because first will be name or path of the program
- invalid_args = Exit 1 "Error: Please provide two integers between -1000 and 1000 as arguments."
- invalid_num_str = Exit 1 "Error: Invalid number format. Please provide integers between -1000 and 1000."
+ arg_range_min_str = Inspect.to_str(arg_range_min)
+ arg_range_max_str = Inspect.to_str(arg_range_max)
+ # TODO this function should not use Exit for modularity. Only perform this change after static dispatch has landed!
+ invalid_args = Exit(1, "Error: Please provide two integers between ${arg_range_min_str} and ${arg_range_max_str} as arguments.")
+ invalid_num_str = Exit(1, "Error: Invalid number format. Please provide integers between ${arg_range_min_str} and ${arg_range_max_str}.")
args =
- if List.len raw_args != 3 then
- return Err invalid_args
+ if List.len(raw_args) != expected_nr_of_args then
+ return Err(invalid_args)
else
- List.map raw_args Arg.display
+ List.map(raw_args, Arg.display)
- a_result = List.get args 1 |> Result.try Str.toI32
- b_result = List.get args 2 |> Result.try Str.toI32
+ a_result = List.get(args, 1) |> Result.try(Str.to_i32)
+ b_result = List.get(args, 2) |> Result.try(Str.to_i32)
when (a_result, b_result) is
- (Ok a, Ok b) ->
- if a < -1000 || a > 1000 || b < -1000 || b > 1000 then
- Err invalid_num_str
+ (Ok(a), Ok(b)) ->
+ if a < arg_range_min or a > arg_range_max or b < arg_range_min or b > arg_range_max then
+ Err(invalid_num_str)
else
- Ok { a, b }
+ Ok({ a, b })
- _ -> Err invalid_num_str
+ _ -> Err(invalid_num_str)
diff --git a/examples/BasicDict/BasicDict.roc b/examples/BasicDict/BasicDict.roc
index 2d510b0..3ba48dd 100644
--- a/examples/BasicDict/BasicDict.roc
+++ b/examples/BasicDict/BasicDict.roc
@@ -6,56 +6,56 @@ module []
# Below we use a Str key for the fruit name, and a U64 value for the fruit count.
fruit_dict : Dict Str U64
fruit_dict =
- Dict.empty {}
- |> Dict.insert "Apple" 3
- |> Dict.insert "Banana" 2
+ Dict.empty({})
+ |> Dict.insert("Apple", 3)
+ |> Dict.insert("Banana", 2)
expect
# get the value for a key
# Dict.get returns a Result with either `Ok value` or `Err KeyNotFound`
- Dict.get fruit_dict "Apple" == (Ok 3)
+ Dict.get(fruit_dict, "Apple") == (Ok(3))
expect
# get the length (number of key-value pairs) of a Dict
- Dict.len fruit_dict == 2
+ Dict.len(fruit_dict) == 2
expect
# convert Dict to a Str
- Inspect.toStr fruit_dict == "{\"Apple\": 3, \"Banana\": 2}"
+ Inspect.to_str(fruit_dict) == "{\"Apple\": 3, \"Banana\": 2}"
expect
# get all the keys
- Dict.keys fruit_dict == ["Apple", "Banana"]
+ Dict.keys(fruit_dict) == ["Apple", "Banana"]
expect
# get all the values
- Dict.values fruit_dict == [3, 2]
+ Dict.values(fruit_dict) == [3, 2]
expect
# convert to a list of tuples
- Dict.toList fruit_dict == [("Apple", 3), ("Banana", 2)]
+ Dict.to_list(fruit_dict) == [("Apple", 3), ("Banana", 2)]
expect
# remove a key-value pair
- Dict.remove fruit_dict "Apple"
- |> Dict.remove "Banana"
- |> Dict.isEmpty
+ Dict.remove(fruit_dict, "Apple")
+ |> Dict.remove("Banana")
+ |> Dict.is_empty
expect
# update the value of a Dict
updated_dict =
- Dict.update fruit_dict "Apple" add_fruit
+ Dict.update(fruit_dict, "Apple", add_fruit)
# We need to account for the case when a key (=fruit) is not in the Dict.
# So we need a function like this:
add_fruit : Result U64 [Missing] -> Result U64 [Missing]
- add_fruit = \value_tag ->
+ add_fruit = |value_tag|
when value_tag is
# If the fruit is not in the dict (=missing), we set the count to 1
- Err Missing -> Ok 1
- # If the fruit is in the dict (=present), we increase the count
- Ok count -> Ok (count + 1)
+ Err(Missing) -> Ok(1)
+ # If the fruit is in the dict, we increase the count
+ Ok(count) -> Ok((count + 1))
- Dict.get updated_dict "Apple" == (Ok 4)
+ Dict.get(updated_dict, "Apple") == (Ok(4))
# see https://www.roc-lang.org/builtins/Dict for more
diff --git a/examples/BasicDict/README.md b/examples/BasicDict/README.md
index 40bba02..38b4b0a 100644
--- a/examples/BasicDict/README.md
+++ b/examples/BasicDict/README.md
@@ -10,11 +10,11 @@ A `Dict` (dictionary) lets you save a value under a key, so that you end up with
For example, you can create a Dict to keep track of how much fruit you have:
```roc
-fruitDict : Dict Str U64
-fruitDict =
- Dict.empty {}
- |> Dict.insert "Apple" 3
- |> Dict.insert "Banana" 2
+fruit_dict : Dict Str U64
+fruit_dict =
+ Dict.empty({})
+ |> Dict.insert("Apple", 3)
+ |> Dict.insert("Banana", 2)
```
## Basic Dict Examples
diff --git a/examples/CommandLineArgs/main.roc b/examples/CommandLineArgs/main.roc
index 4f3644d..93f528d 100644
--- a/examples/CommandLineArgs/main.roc
+++ b/examples/CommandLineArgs/main.roc
@@ -1,21 +1,20 @@
# Run with `roc ./examples/CommandLineArgs/main.roc some_argument`
-# !! This currently does not work in combination with --linker=legacy, see https://github.com/roc-lang/basic-cli/issues/82
app [main!] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
}
-import pf.Stdout
-import pf.Arg
+import cli.Stdout
+import cli.Arg
-main! = \raw_args ->
- args = List.map raw_args Arg.display
+main! = |raw_args|
+ args = List.map(raw_args, Arg.display)
# get the second argument, the first is the executable's path
- arg_result = List.get args 1 |> Result.mapErr (\_ -> ZeroArgsGiven)
+ arg_result = List.get(args, 1) |> Result.map_err(ZeroArgsGiven)
when arg_result is
- Err ZeroArgsGiven ->
- Err (Exit 1 "Error ZeroArgsGiven:\n\tI expected one argument, but I got none.\n\tRun the app like this: `roc main.roc -- input.txt`")
+ Err(ZeroArgsGiven(_)) ->
+ Err(Exit(1, "Error ZeroArgsGiven:\n\tI expected one argument, but I got none.\n\tRun the app like this: `roc main.roc -- input.txt`"))
- Ok first_argument ->
- Stdout.line! "received argument: $(first_argument)"
+ Ok(first_argument) ->
+ Stdout.line!("received argument: ${first_argument}")
diff --git a/examples/CommandLineArgsFile/main.roc b/examples/CommandLineArgsFile/main.roc
index c3ab3ed..2234f2c 100644
--- a/examples/CommandLineArgsFile/main.roc
+++ b/examples/CommandLineArgsFile/main.roc
@@ -1,44 +1,43 @@
# Run with `roc ./examples/CommandLineArgsFile/main.roc -- examples/CommandLineArgsFile/input.txt`
-# This currently does not work in combination with --linker=legacy, see https://github.com/roc-lang/basic-cli/issues/82
app [main!] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
}
-import pf.Stdout
-import pf.Path exposing [Path]
-import pf.Arg
+import cli.Stdout
+import cli.Path exposing [Path]
+import cli.Arg
-main! = \raw_args ->
+main! = |raw_args|
# read all command line arguments
- args = List.map raw_args Arg.display
+ args = List.map(raw_args, Arg.display)
# get the second argument, the first is the executable's path
- arg_result = List.get args 1 |> Result.mapErr \_ -> ZeroArgsGiven
+ arg_result = List.get(args, 1) |> Result.map_err(ZeroArgsGiven)
when arg_result is
- Ok arg ->
- file_content_str = try read_file_to_str! (Path.from_str arg)
+ Ok(arg) ->
+ file_content_str = read_file_to_str!(Path.from_str(arg))?
- Stdout.line! "file content: $(file_content_str)"
+ Stdout.line!("file content: ${file_content_str}")
- Err ZeroArgsGiven ->
- Err (Exit 1 "Error ZeroArgsGiven:\n\tI expected one argument, but I got none.\n\tRun the app like this: `roc main.roc -- path/to/input.txt`")
+ Err(ZeroArgsGiven(_)) ->
+ Err(Exit(1, "Error ZeroArgsGiven:\n\tI expected one argument, but I got none.\n\tRun the app like this: `roc main.roc -- path/to/input.txt`"))
# reads a file and puts all lines in one Str
read_file_to_str! : Path => Result Str [ReadFileErr Str]_
-read_file_to_str! = \path ->
+read_file_to_str! = |path|
path
|> Path.read_utf8!
- |> Result.mapErr \file_read_err ->
- path_str = Path.display path
+ |> Result.map_err(
+ |file_read_err|
+ path_str = Path.display(path)
- when file_read_err is
- FileReadErr _ read_err ->
- read_err_str = Inspect.toStr read_err
+ when file_read_err is
+ FileReadErr(_, read_err) ->
+ ReadFileErr("Failed to read file:\n\t${path_str}\nWith error:\n\t${Inspect.to_str(read_err)}")
- ReadFileErr "Failed to read file:\n\t$(path_str)\nWith error:\n\t$(read_err_str)"
-
- FileReadUtf8Err _ _ ->
- ReadFileErr "I could not read the file:\n\t$(path_str)\nIt contains charcaters that are not valid UTF-8:\n\t- Check if the file is encoded using a different format and convert it to UTF-8.\n\t- Check if the file is corrupted.\n\t- Find the characters that are not valid UTF-8 and fix or remove them."
+ FileReadUtf8Err(_, _) ->
+ ReadFileErr("I could not read the file:\n\t${path_str}\nIt contains charcaters that are not valid UTF-8:\n\t- Check if the file is encoded using a different format and convert it to UTF-8.\n\t- Check if the file is corrupted.\n\t- Find the characters that are not valid UTF-8 and fix or remove them."),
+ )
diff --git a/examples/CustomInspect/OpaqueTypes.roc b/examples/CustomInspect/OpaqueTypes.roc
index 93cf30f..8f3b2ef 100644
--- a/examples/CustomInspect/OpaqueTypes.roc
+++ b/examples/CustomInspect/OpaqueTypes.roc
@@ -7,28 +7,28 @@ Color := [
Blue,
]
implements [
- Inspect { toInspector: color_inspector },
+ Inspect { to_inspector: color_inspector },
]
-color_inspector : Color -> Inspector f where f implements InspectFormatter
-color_inspector = \@Color color ->
+color_inspector : Color -> Inspector fmt where fmt implements InspectFormatter
+color_inspector = |@Color(color)|
when color is
- Red -> Inspect.str "_RED_"
- Green -> Inspect.str "_GREEN_"
- Blue -> Inspect.str "_BLUE_"
+ Red -> Inspect.str("_RED_")
+ Green -> Inspect.str("_GREEN_")
+ Blue -> Inspect.str("_BLUE_")
-expect Inspect.toStr (@Color Red) == "\"_RED_\""
-expect Inspect.toStr (@Color Green) == "\"_GREEN_\""
-expect Inspect.toStr (@Color Blue) == "\"_BLUE_\""
+expect Inspect.to_str(@Color(Red)) == "\"_RED_\""
+expect Inspect.to_str(@Color(Green)) == "\"_GREEN_\""
+expect Inspect.to_str(@Color(Blue)) == "\"_BLUE_\""
### end snippet color
### start snippet secret
MySecret := Str implements [
- Inspect { toInspector: my_secret_inspector },
+ Inspect { to_inspector: my_secret_inspector },
]
-my_secret_inspector : MySecret -> Inspector f where f implements InspectFormatter
-my_secret_inspector = \@MySecret _ -> Inspect.str "******* REDACTED *******"
+my_secret_inspector : MySecret -> Inspector fmt where fmt implements InspectFormatter
+my_secret_inspector = |@MySecret(_)| Inspect.str("******* REDACTED *******")
-expect Inspect.toStr (@MySecret "password1234") == "\"******* REDACTED *******\""
+expect Inspect.to_str(@MySecret("password1234")) == "\"******* REDACTED *******\""
### end snippet secret
diff --git a/examples/DesugaringTry/README.md b/examples/DesugaringTry/README.md
index f94a0e3..f6e854d 100644
--- a/examples/DesugaringTry/README.md
+++ b/examples/DesugaringTry/README.md
@@ -8,7 +8,7 @@
convenient way without adding new functionality to the language itself.
-Desugaring converts syntax sugar (like `x + 1`) into more fundamental operations (like `Num.add x 1`).
+Desugaring converts syntax sugar (like `x + 1`) into more fundamental operations (like `Num.add(x, 1)`).
Let's see how `?` is desugared. In this example we will extract the name and birth year from a
string like `"Alice was born in 1990"`.
@@ -18,18 +18,20 @@ file:main.roc:snippet:question
After desugaring, this becomes:
```roc
-file:main.roc:snippet:try
+file:main.roc:snippet:desugared
```
-[Result.try](https://www.roc-lang.org/builtins/Result#try) takes the success
-value from a given Result and uses that to generate a new Result.
-It's type is `Result a err, (a -> Result b err) -> Result b err`.
+So `birth_year = Str.to_u16(birth_year_str)?` is converted to
-`birthYear = Str.toU16? birthYearStr` is converted to `Str.toU16 birthYearStr |> Result.try \birthYear ->`.
+```roc
+when Str.to_u16(birth_year_str) is
+ Err(err2) -> return Err(err2)
+ Ok(birth_year) -> birth_year
+```
As you can see, the first version is a lot nicer!
-Thanks to `?`, you can write code in a mostly familiar way while also getting the benefits of Roc's
-error handling.
+Thanks to `?`, you can write code in a familiar way and you get the benefits of Roc's
+error handling to drastically reduce the likelihood of crashes.
## Full Code
```roc
@@ -42,6 +44,6 @@ Run this from the directory that has `main.roc` in it:
```
$ roc main.roc
-(Ok {birthYear: 1990, name: "Alice"})
-(Ok {birthYear: 1990, name: "Alice"})
+Ok({birth_year: 1990, name: "Alice"})
+Ok({birth_year: 1990, name: "Alice"})
```
diff --git a/examples/DesugaringTry/main.roc b/examples/DesugaringTry/main.roc
index bec2ddd..04aca35 100644
--- a/examples/DesugaringTry/main.roc
+++ b/examples/DesugaringTry/main.roc
@@ -1,33 +1,35 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
-import pf.Stdout
+import cli.Stdout
-main! = \_args ->
- try Stdout.line! (Inspect.toStr (parse_name_and_year "Alice was born in 1990"))
- try Stdout.line! (Inspect.toStr (parse_name_and_year_try "Alice was born in 1990"))
+main! = |_args|
+ Stdout.line!(Inspect.to_str(parse_name_and_year("Alice was born in 1990")))?
+ Stdout.line!(Inspect.to_str(parse_name_and_year_try("Alice was born in 1990")))?
- Ok {}
+ Ok({})
### start snippet question
parse_name_and_year : Str -> Result { name : Str, birth_year : U16 } _
-parse_name_and_year = \str ->
- { before: name, after: birth_year_str } = Str.splitFirst? str " was born in "
- birth_year = Str.toU16? birth_year_str
- Ok { name, birth_year }
+parse_name_and_year = |str|
+ { before: name, after: birth_year_str } = Str.split_first(str, " was born in ")?
+ birth_year = Str.to_u16(birth_year_str)?
+ Ok({ name, birth_year })
### end snippet question
-parse_name_and_year_try = \str ->
- ### start snippet try
- str
- |> Str.splitFirst " was born in "
- |> Result.try \{ before: name, after: birth_year_str } ->
- Str.toU16 birth_year_str
- |> Result.try \birth_year ->
- Ok { name, birth_year }
-### end snippet try
-
-expect
- parse_name_and_year "Alice was born in 1990" == Ok { name: "Alice", birth_year: 1990 }
-
-expect
- parse_name_and_year_try "Alice was born in 1990" == Ok { name: "Alice", birth_year: 1990 }
+### start snippet desugared
+parse_name_and_year_try = |str|
+ when Str.split_first(str, " was born in ") is
+ Err(err1) ->
+ return Err(err1)
+
+ Ok({ before: name, after: birth_year_str }) ->
+ when Str.to_u16(birth_year_str) is
+ Err(err2) ->
+ return Err(err2)
+
+ Ok(birth_year) ->
+ Ok({ name, birth_year })
+### end snippet desugared
+
+expect parse_name_and_year("Alice was born in 1990") == Ok({ name: "Alice", birth_year: 1990 })
+expect parse_name_and_year_try("Alice was born in 1990") == Ok({ name: "Alice", birth_year: 1990 })
diff --git a/examples/DotNetPlatform/README.md b/examples/DotNetPlatform/README.md
index 89b434c..901a966 100644
--- a/examples/DotNetPlatform/README.md
+++ b/examples/DotNetPlatform/README.md
@@ -41,12 +41,12 @@ To run:
$ cd platform
$ dotnet run
```
-This should print "Hello from .NET".
+This should print "Hello from .NET" and "Hi from roc! (in a .NET platform) 🔥🦅🔥".
## Build & Run Binary
-If you want to build a binary for the app using native AOT:
+To build a binary for the app using Ahead-Of-Time compilation:
1. Publish the dotnet app
```cli
diff --git a/examples/DotNetPlatform/platform/Program.cs b/examples/DotNetPlatform/platform/Program.cs
index e63ce1c..644a627 100644
--- a/examples/DotNetPlatform/platform/Program.cs
+++ b/examples/DotNetPlatform/platform/Program.cs
@@ -30,7 +30,7 @@ static IntPtr CustomResolver(string libraryName, Assembly assembly, DllImportSea
public static partial class Platform
{
- [LibraryImport("interop", EntryPoint = "roc__mainForHost_1_exposed_generic")]
+ [LibraryImport("interop", EntryPoint = "roc__main_for_host_1_exposed_generic")]
internal static partial void MainFromRoc(out RocStr rocStr);
}
diff --git a/examples/DotNetPlatform/platform/main.roc b/examples/DotNetPlatform/platform/main.roc
index fc69370..5fc46d9 100644
--- a/examples/DotNetPlatform/platform/main.roc
+++ b/examples/DotNetPlatform/platform/main.roc
@@ -3,7 +3,7 @@ platform "dotnetplatform"
exposes []
packages {}
imports []
- provides [mainForHost]
+ provides [main_for_host]
-mainForHost : Str
-mainForHost = main
+main_for_host : Str
+main_for_host = main
diff --git a/examples/ElmWebApp/backend.roc b/examples/ElmWebApp/backend.roc
index 27f0d7e..860bf81 100644
--- a/examples/ElmWebApp/backend.roc
+++ b/examples/ElmWebApp/backend.roc
@@ -1,10 +1,10 @@
-app [Model, server] {
- pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.8.0/jz2EfGAtz_y06nN7f8tU9AvmzhKK-jnluXQQGa9rZoQ.tar.br",
+app [Model, init!, respond!] {
+ web: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.12.0/Q4h_In-sz1BqAvlpmCsBHhEJnn_YvfRRMiNACB_fBbk.tar.br",
}
-import pf.Stdout
-import pf.Http exposing [Request, Response]
-import pf.Utc
+import web.Stdout
+import web.Http exposing [Request, Response]
+import web.Utc
# [backend](https://chatgpt.com/share/7ac35a32-dab5-46d0-bb17-9d584469556f) Roc server
@@ -13,24 +13,26 @@ Model : {}
# With `init` you can set up a database connection once at server startup,
# generate css by running `tailwindcss`,...
-# In this case we don't have anything to initialize, so it is just `Task.ok {}`.
+# In this case we don't have anything to initialize, so it is just `Ok({})`.
-server = { init: Task.ok {}, respond }
+init! = |{}| Ok({})
-respond : Request, Model -> Task Response [ServerErr Str]_
-respond = \req, _ ->
+respond! : Request, Model => Result Response [ServerErr Str]_
+respond! = |req, _|
# Log request datetime, method and url
- datetime = Utc.now! |> Utc.toIso8601Str
-
- Stdout.line! "$(datetime) $(Http.methodToStr req.method) $(req.url)"
-
- Task.ok {
- status: 200,
- headers: [
- # !!
- # Change http://localhost:8001 to your domain for production usage
- # !!
- { name: "Access-Control-Allow-Origin", value: "http://localhost:8001" },
- ],
- body: Str.toUtf8 "Hi, Elm! This is from Roc: 🎁\n",
- }
+ datetime = Utc.to_iso_8601(Utc.now!({}))
+
+ Stdout.line!("${datetime} ${Inspect.to_str(req.method)} ${req.uri}")?
+
+ Ok(
+ {
+ status: 200,
+ headers: [
+ # !!
+ # Change http://localhost:8001 to your domain for production usage
+ # !!
+ { name: "Access-Control-Allow-Origin", value: "http://localhost:8001" },
+ ],
+ body: Str.to_utf8("Hi, Elm! This is from Roc: 🎁\n"),
+ },
+ )
diff --git a/examples/EncodeDecode/README.md b/examples/EncodeDecode/README.md
index 0ad9617..955b6cb 100644
--- a/examples/EncodeDecode/README.md
+++ b/examples/EncodeDecode/README.md
@@ -1,8 +1,9 @@
# Encoding & Decoding Abilities
-An example for how to implement the builtin `Encoding` and `Decoding` abilities for an opaque type.
+An example for how to implement the builtin `Encoding` and `Decoding` abilities for an opaque type (`ItemKind`).
-Implementing these abilites for an opaque type like `ItemKind`, enables it to be used seamlessly within other data structures. This is useful when you would like to provide a custom mapping, such as in this example, between an integer and a tag union.
+Implementing these abilites for an opaque type like `ItemKind`, enables it to be used seamlessly within other data structures.
+This is useful when you would like to provide a custom mapping, such as in this example, between an integer and a [tag union](https://www.roc-lang.org/tutorial#tag-union-types).
## Implementation
```roc
@@ -29,6 +30,7 @@ $ roc dev
(@ItemKind Class)
(@ItemKind Interface)
(@ItemKind Module)
+(@ItemKind Property)
```
You can also use `roc test` to run the tests.
diff --git a/examples/EncodeDecode/main.roc b/examples/EncodeDecode/main.roc
index d436efc..3dbbf2e 100644
--- a/examples/EncodeDecode/main.roc
+++ b/examples/EncodeDecode/main.roc
@@ -1,10 +1,10 @@
app [main!] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
- json: "https://github.com/lukewilliamboswell/roc-json/releases/download/0.11.0/z45Wzc-J39TLNweQUoLw3IGZtkQiEN3lTBv3BXErRjQ.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
+ json: "https://github.com/lukewilliamboswell/roc-json/releases/download/0.12.0/1trwx8sltQ-e9Y2rOB4LWUWLS_sFVyETK8Twl0i9qpw.tar.gz",
}
import json.Json
-import pf.Stdout
+import cli.Stdout
### start snippet impl
ItemKind := [
@@ -21,54 +21,59 @@ ItemKind := [
]
implements [
Decoding { decoder: decode_items },
- Encoding { toEncoder: encode_items },
+ Encoding { to_encoder: encode_items },
Inspect,
Eq,
]
+encode_items : ItemKind -> Encoder fmt where fmt implements EncoderFormatting
+encode_items = |@ItemKind(kind)|
+ Encode.u32(
+ when kind is
+ Text -> 1
+ Method -> 2
+ Function -> 3
+ Constructor -> 4
+ Field -> 5
+ Variable -> 6
+ Class -> 7
+ Interface -> 8
+ Module -> 9
+ Property -> 10,
+ )
+
+decode_items : Decoder ItemKind _
+decode_items =
+ Decode.custom(
+ |bytes, fmt|
+ # Helper function to wrap our [tag](https://www.roc-lang.org/tutorial#tags)
+ ok = |tag| Ok(@ItemKind(tag))
+
+ bytes
+ |> Decode.from_bytes_partial(fmt)
+ |> try_map_result(
+ |num|
+ when num is
+ 1 -> ok(Text)
+ 2 -> ok(Method)
+ 3 -> ok(Function)
+ 4 -> ok(Constructor)
+ 5 -> ok(Field)
+ 6 -> ok(Variable)
+ 7 -> ok(Class)
+ 8 -> ok(Interface)
+ 9 -> ok(Module)
+ 10 -> ok(Property)
+ _ -> Err(TooShort),
+ ),
+ )
+
+# Converts `DecodeResult U32` to `DecodeResult ItemKind` using a given function
try_map_result : DecodeResult U32, (U32 -> Result ItemKind DecodeError) -> DecodeResult ItemKind
-try_map_result = \decoded, mapper ->
+try_map_result = |decoded, num_to_item_kind_fun|
when decoded.result is
- Err e -> { result: Err e, rest: decoded.rest }
- Ok res -> { result: mapper res, rest: decoded.rest }
-
-decode_items : Decoder ItemKind fmt where fmt implements DecoderFormatting
-decode_items = Decode.custom \bytes, fmt ->
- # Helper function to wrap our tag
- ok = \tag -> Ok (@ItemKind tag)
-
- bytes
- |> Decode.fromBytesPartial fmt
- |> try_map_result \val ->
- when val is
- 1 -> ok Text
- 2 -> ok Method
- 3 -> ok Function
- 4 -> ok Constructor
- 5 -> ok Field
- 6 -> ok Variable
- 7 -> ok Class
- 8 -> ok Interface
- 9 -> ok Module
- 10 -> ok Property
- _ -> Err TooShort
-
-encode_items : ItemKind -> Encoder fmt where fmt implements EncoderFormatting
-encode_items = \@ItemKind val ->
- Encode.u32
- (
- when val is
- Text -> 1
- Method -> 2
- Function -> 3
- Constructor -> 4
- Field -> 5
- Variable -> 6
- Class -> 7
- Interface -> 8
- Module -> 9
- Property -> 10
- )
+ Err(e) -> { result: Err(e), rest: decoded.rest }
+ Ok(res) -> { result: num_to_item_kind_fun(res), rest: decoded.rest }
### end snippet impl
@@ -77,41 +82,42 @@ encode_items = \@ItemKind val ->
# make a list of ItemKind's
original_list : List ItemKind
original_list = [
- @ItemKind Text,
- @ItemKind Method,
- @ItemKind Function,
- @ItemKind Constructor,
- @ItemKind Field,
- @ItemKind Variable,
- @ItemKind Class,
- @ItemKind Interface,
- @ItemKind Module,
- @ItemKind Property,
+ @ItemKind(Text),
+ @ItemKind(Method),
+ @ItemKind(Function),
+ @ItemKind(Constructor),
+ @ItemKind(Field),
+ @ItemKind(Variable),
+ @ItemKind(Class),
+ @ItemKind(Interface),
+ @ItemKind(Module),
+ @ItemKind(Property),
]
-# encode them into JSON
+# encode them into JSON bytes
encoded_bytes : List U8
-encoded_bytes = Encode.toBytes original_list Json.utf8
+encoded_bytes = Encode.to_bytes(original_list, Json.utf8)
-# test we have encoded correctly
-expect encoded_bytes == original_bytes
+# check that encoding is correct
+expect
+ expected_bytes : List U8
+ expected_bytes = "[1,2,3,4,5,6,7,8,9,10]" |> Str.to_utf8
-# take a JSON encoded list
-original_bytes : List U8
-original_bytes = "[1,2,3,4,5,6,7,8,9,10]" |> Str.toUtf8
+ encoded_bytes == expected_bytes
-# decode into a list of ItemKind's
+# decode back to a list of ItemKind's
decoded_list : List ItemKind
-decoded_list = Decode.fromBytes original_bytes Json.utf8 |> Result.withDefault []
+decoded_list = Decode.from_bytes(encoded_bytes, Json.utf8) |> Result.with_default([])
+# don't use `Result.with_default([])` for professional applications; check https://www.roc-lang.org/examples/ErrorHandling/README.html
-# test we have decoded correctly
+# check that decoding is correct
expect decoded_list == original_list
-main! = \_args ->
- # debug print decoded items to stdio
+main! = |_args|
+ # prints decoded items to stdout
decoded_list
- |> List.map Inspect.toStr
- |> Str.joinWith "\n"
+ |> List.map(Inspect.to_str)
+ |> Str.join_with("\n")
|> Stdout.line!
### end snippet demo
diff --git a/examples/ErrorHandling/README.md b/examples/ErrorHandling/README.md
index f0a2aef..6d262e1 100644
--- a/examples/ErrorHandling/README.md
+++ b/examples/ErrorHandling/README.md
@@ -1,6 +1,6 @@
# Error handling with try and Result
-A more complex "real world" example that demonstrates the use of `try`, `Result` and error handling in Roc.
+A more complex "real world" example that demonstrates the use of `Result`, `?` and error handling in Roc.
## Full Code
@@ -13,7 +13,7 @@ file:main.roc
Run this from the directory that has `main.roc` in it:
```sh
-$ HELLO=1 roc examples/Tasks/main.roc -- "https://www.roc-lang.org" roc.html
+$ HELLO=1 roc examples/ErrorHandling/main.roc -- "https://www.roc-lang.org" roc.html
HELLO env var was set to 1
Fetching content from https://www.roc-lang.org...
Saving url HTML to roc.html...
diff --git a/examples/ErrorHandling/main.roc b/examples/ErrorHandling/main.roc
index b1a854c..4aaf498 100644
--- a/examples/ErrorHandling/main.roc
+++ b/examples/ErrorHandling/main.roc
@@ -1,91 +1,100 @@
app [main!] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
}
-import pf.Stdout
-import pf.Arg exposing [Arg]
-import pf.Env
-import pf.Http
-import pf.Dir
-import pf.Utc exposing [Utc]
-import pf.Path exposing [Path]
+import cli.Stdout
+import cli.Arg exposing [Arg]
+import cli.Env
+import cli.Http
+import cli.Dir
+import cli.Utc exposing [Utc]
+import cli.Path exposing [Path]
usage = "HELLO=1 roc main.roc -- \"https://www.roc-lang.org\" roc.html"
main! : List Arg => Result {} _
-main! = \args ->
+main! = |args|
# Get time since [Unix Epoch](https://en.wikipedia.org/wiki/Unix_time)
start_time : Utc
- start_time = Utc.now! {}
+ start_time = Utc.now!({})
# Read the HELLO environment variable
hello_env : Str
hello_env =
- read_env_var! "HELLO"
- |> try
- |> \msg -> if Str.isEmpty msg then "was empty" else "was set to $(msg)"
+ read_env_var!("HELLO")?
+ |> |env_var_content|
+ if Str.is_empty(env_var_content) then
+ "was empty"
+ else
+ "was set to ${env_var_content}"
- try Stdout.line! "HELLO env var $(hello_env)"
+ Stdout.line!("HELLO env var ${hello_env}")?
# Read command line arguments
- { url, output_path } = try parse_args! args
+ { url, output_path } = parse_args!(args)?
- try Stdout.line! "Fetching content from $(url)..."
+ Stdout.line!("Fetching content from ${url}...")?
# Fetch the provided url using HTTP
html_str : Str
- html_str = try fetch_html! url
+ html_str = fetch_html!(url)?
- try Stdout.line! "Saving url HTML to $(Path.display output_path)..."
+ Stdout.line!("Saving url HTML to ${Path.display(output_path)}...")?
# Write HTML string to a file
- Path.write_utf8! html_str output_path
- |> Result.mapErr \_ -> FailedToWriteFile "Failed to write to file $(Path.display output_path), usage: $(usage)"
- |> try
+ Result.map_err(
+ Path.write_utf8!(html_str, output_path),
+ |_| FailedToWriteFile("Failed to write to file ${Path.display(output_path)}, usage: ${usage}"),
+ )?
# Print contents of current working directory
- try list_cwd_contents! {}
+ list_cwd_contents!({})?
end_time : Utc
- end_time = Utc.now! {}
+ end_time = Utc.now!({})
- run_duration = Utc.delta_as_millis start_time end_time
+ run_duration = Utc.delta_as_millis(start_time, end_time)
- try Stdout.line! "Run time: $(Num.toStr run_duration) ms"
+ Stdout.line!("Run time: ${Num.to_str(run_duration)} ms")?
- try Stdout.line! "Done"
+ Stdout.line!("Done")?
- Ok {}
+ Ok({})
parse_args! : List Arg => Result { url : Str, output_path : Path } _
-parse_args! = \args ->
- when List.map args Arg.display is
+parse_args! = |args|
+ when List.map(args, Arg.display) is
[_, first, second, ..] ->
- Ok { url: first, output_path: Path.from_str second }
+ Ok({ url: first, output_path: Path.from_str(second) })
_ ->
- Err (FailedToReadArgs "Failed to read command line arguments, usage: $(usage)")
+ Err(FailedToReadArgs("Failed to read command line arguments, usage: ${usage}"))
read_env_var! : Str => Result Str []
-read_env_var! = \envVarName ->
- when Env.var! envVarName is
- Ok envVarStr if !(Str.isEmpty envVarStr) -> Ok envVarStr
- _ -> Ok ""
+read_env_var! = |env_var_name|
+ when Env.var!(env_var_name) is
+ Ok(env_var_str) if !Str.is_empty(env_var_str) -> Ok(env_var_str)
+ _ -> Ok("")
fetch_html! : Str => Result Str _
-fetch_html! = \url ->
- Http.get_utf8! url
- |> Result.mapErr \err -> FailedToFetchHtml "Failed to fetch URL $(Inspect.toStr err), usage: $(usage)"
+fetch_html! = |url|
+ Http.get_utf8!(url)
+ |> Result.map_err(|err| FailedToFetchHtml("Failed to fetch URL ${Inspect.to_str(err)}, usage: ${usage}"))
+# effects need to be functions so we use the empty input type `{}`
list_cwd_contents! : {} => Result {} _
-list_cwd_contents! = \_ ->
+list_cwd_contents! = |_|
- dirContents =
- Dir.list! "."
- |> Result.mapErr \_ -> FailedToListCwd "Failed to list contents of current directory, usage: $(usage)"
- |> try
+ dir_contents =
+ Result.map_err(
+ Dir.list!("."),
+ |_| FailedToListCwd("Failed to list contents of current directory, usage: ${usage}"),
+ )?
- contentsStr = List.map dirContents Path.display |> Str.joinWith ","
+ contents_str =
+ dir_contents
+ |> List.map(Path.display)
+ |> Str.join_with(",")
- Stdout.line! "Contents of current directory: $(contentsStr)"
+ Stdout.line!("Contents of current directory: ${contents_str}")
diff --git a/examples/FizzBuzz/main.roc b/examples/FizzBuzz/main.roc
index 9f85712..d21081a 100644
--- a/examples/FizzBuzz/main.roc
+++ b/examples/FizzBuzz/main.roc
@@ -1,11 +1,11 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
-import pf.Stdout
+import cli.Stdout
-main! = \_args ->
- List.range { start: At 1, end: At 100 }
- |> List.map fizz_buzz
- |> Str.joinWith ","
+main! = |_args|
+ List.range({ start: At(1), end: At(100) })
+ |> List.map(fizz_buzz)
+ |> Str.join_with(",")
|> Stdout.line!
## Determine the FizzBuzz value for a given integer.
@@ -13,31 +13,31 @@ main! = \_args ->
## multiples of 5, "FizzBuzz" for multiples of both
## 3 and 5, and the original number for anything else.
fizz_buzz : I32 -> Str
-fizz_buzz = \n ->
+fizz_buzz = |n|
fizz = n % 3 == 0
buzz = n % 5 == 0
- if fizz && buzz then
+ if fizz and buzz then
"FizzBuzz"
else if fizz then
"Fizz"
else if buzz then
"Buzz"
else
- Num.toStr n
+ Num.to_str(n)
## Test Case 1: not a multiple of 3 or 5
-expect fizz_buzz 1 == "1"
-expect fizz_buzz 7 == "7"
+expect fizz_buzz(1) == "1"
+expect fizz_buzz(7) == "7"
## Test Case 2: multiple of 3
-expect fizz_buzz 3 == "Fizz"
-expect fizz_buzz 9 == "Fizz"
+expect fizz_buzz(3) == "Fizz"
+expect fizz_buzz(9) == "Fizz"
## Test Case 3: multiple of 5
-expect fizz_buzz 5 == "Buzz"
-expect fizz_buzz 20 == "Buzz"
+expect fizz_buzz(5) == "Buzz"
+expect fizz_buzz(20) == "Buzz"
## Test Case 4: multiple of both 3 and 5
-expect fizz_buzz 15 == "FizzBuzz"
-expect fizz_buzz 45 == "FizzBuzz"
+expect fizz_buzz(15) == "FizzBuzz"
+expect fizz_buzz(45) == "FizzBuzz"
diff --git a/examples/GoPlatform/README.md b/examples/GoPlatform/README.md
index 3a1a0b5..a6bf595 100644
--- a/examples/GoPlatform/README.md
+++ b/examples/GoPlatform/README.md
@@ -72,7 +72,7 @@ $ roc build --bundle .tar.br platform/main.roc
4. Now you can use the platform from inside a Roc file with:
```roc
-app [goUsingRocApp] { pf: platform "YOUR_URL" }
+app [main] { pf: platform "YOUR_URL" }
```
‼ This build procedure only builds the platform for your kind of operating system and architecture. If you want to support users on all Roc supported operating systems and architectures, you'll need [this kind of setup](https://github.com/roc-lang/roc/blob/main/.github/workflows/basic_cli_build_release.yml).
diff --git a/examples/GoPlatform/platform/host.h b/examples/GoPlatform/platform/host.h
index 7714980..f8b6d01 100644
--- a/examples/GoPlatform/platform/host.h
+++ b/examples/GoPlatform/platform/host.h
@@ -6,4 +6,4 @@ struct RocStr {
size_t capacity;
};
-extern void roc__mainForHost_1_exposed_generic(const struct RocStr *data);
+extern void roc__main_for_host_1_exposed_generic(const struct RocStr *data);
diff --git a/examples/GoPlatform/platform/main.go b/examples/GoPlatform/platform/main.go
index 7b2555a..376e6ad 100644
--- a/examples/GoPlatform/platform/main.go
+++ b/examples/GoPlatform/platform/main.go
@@ -14,7 +14,7 @@ import (
func main() {
var str C.struct_RocStr
- C.roc__mainForHost_1_exposed_generic(&str)
+ C.roc__main_for_host_1_exposed_generic(&str)
fmt.Print(rocStrRead(str))
}
diff --git a/examples/GoPlatform/platform/main.roc b/examples/GoPlatform/platform/main.roc
index 91caf43..1f129f6 100644
--- a/examples/GoPlatform/platform/main.roc
+++ b/examples/GoPlatform/platform/main.roc
@@ -3,7 +3,7 @@ platform "go-platform"
exposes []
packages {}
imports []
- provides [mainForHost]
+ provides [main_for_host]
-mainForHost : Str
-mainForHost = main
+main_for_host : Str
+main_for_host = main
diff --git a/examples/GraphTraversal/Graph.roc b/examples/GraphTraversal/Graph.roc
index 21f0811..0823b57 100644
--- a/examples/GraphTraversal/Graph.roc
+++ b/examples/GraphTraversal/Graph.roc
@@ -1,4 +1,4 @@
-## The Graph interface represents a [graph](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics))
+## The Graph module represents a [graph](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics))
## using an [adjacency list](https://en.wikipedia.org/wiki/Adjacency_list)
## and exposes functions for working with graphs, such as creating one from a list and
## performing a depth-first or breadth-first search.
@@ -16,13 +16,13 @@ Graph a := Dict a (List a) where a implements Eq
## Create a Graph from an adjacency list.
from_list : List (a, List a) -> Graph a
-from_list = \adjacency_list ->
- empty_dict = Dict.withCapacity (List.len adjacency_list)
+from_list = |adjacency_list|
+ empty_dict = Dict.with_capacity(List.len(adjacency_list))
- update = \dict, (vertex, edges) ->
- Dict.insert dict vertex edges
+ update = |dict, (vertex, edges)|
+ Dict.insert(dict, vertex, edges)
- @Graph (List.walk adjacency_list empty_dict update)
+ @Graph(List.walk(adjacency_list, empty_dict, update))
## Create a Graph from an adjacency list.
from_dict : Dict a (List a) -> Graph a
@@ -31,148 +31,148 @@ from_dict = @Graph
## Perform a depth-first search on a graph to find a target vertex.
## [Algorithm animation](https://en.wikipedia.org/wiki/Depth-first_search#/media/File:Depth-First-Search.gif)
##
-## - `isTarget` : A function that returns true if a vertex is the target.
+## - `is_target` : A function that returns true if a vertex is the target.
## - `root` : The starting vertex for the search.
## - `graph` : The graph to perform the search on.
dfs : (a -> Bool), a, Graph a -> Result a [NotFound]
-dfs = \is_target, root, @Graph graph ->
- dfs_helper is_target [root] (Set.empty {}) graph
+dfs = |is_target, root, @Graph(graph)|
+ dfs_helper(is_target, [root], Set.empty({}), graph)
# A helper function for performing the depth-first search.
#
-# `isTarget` : A function that returns true if a vertex is the target.
+# `is_target` : A function that returns true if a vertex is the target.
# `stack` : A List of vertices to visit.
# `visited` : A Set of visited vertices.
# `graph` : The graph to perform the search on.
dfs_helper : (a -> Bool), List a, Set a, Dict a (List a) -> Result a [NotFound]
-dfs_helper = \is_target, stack, visited, graph ->
+dfs_helper = |is_target, stack, visited, graph|
when stack is
[] ->
- Err NotFound
+ Err(NotFound)
[.., current] ->
- rest = List.dropLast stack 1
+ rest = List.drop_last(stack, 1)
- if is_target current then
- Ok current
- else if Set.contains visited current then
- dfs_helper is_target rest visited graph
+ if is_target(current) then
+ Ok(current)
+ else if Set.contains(visited, current) then
+ dfs_helper(is_target, rest, visited, graph)
else
- new_visited = Set.insert visited current
+ new_visited = Set.insert(visited, current)
- when Dict.get graph current is
- Ok neighbors ->
+ when Dict.get(graph, current) is
+ Ok(neighbors) ->
# filter out all visited neighbors
filtered =
neighbors
- |> List.keepIf (\n -> !(Set.contains new_visited n))
+ |> List.keep_if(|n| !(Set.contains(new_visited, n)))
|> List.reverse
# newly explored nodes are added to LIFO stack
- new_stack = List.concat rest filtered
+ new_stack = List.concat(rest, filtered)
- dfs_helper is_target new_stack new_visited graph
+ dfs_helper(is_target, new_stack, new_visited, graph)
- Err KeyNotFound ->
- dfs_helper is_target rest new_visited graph
+ Err(KeyNotFound) ->
+ dfs_helper(is_target, rest, new_visited, graph)
## Perform a breadth-first search on a graph to find a target vertex.
## [Algorithm animation](https://en.wikipedia.org/wiki/Breadth-first_search#/media/File:Animated_BFS.gif)
##
-## - `isTarget` : A function that returns true if a vertex is the target.
+## - `is_target` : A function that returns true if a vertex is the target.
## - `root` : The starting vertex for the search.
## - `graph` : The graph to perform the search on.
bfs : (a -> Bool), a, Graph a -> Result a [NotFound]
-bfs = \is_target, root, @Graph graph ->
- bfs_helper is_target [root] (Set.single root) graph
+bfs = |is_target, root, @Graph(graph)|
+ bfs_helper(is_target, [root], Set.single(root), graph)
# A helper function for performing the breadth-first search.
#
-# `isTarget` : A function that returns true if a vertex is the target.
+# `is_target` : A function that returns true if a vertex is the target.
# `queue` : A List of vertices to visit.
# `seen` : A Set of all seen vertices.
# `graph` : The graph to perform the search on.
bfs_helper : (a -> Bool), List a, Set a, Dict a (List a) -> Result a [NotFound]
-bfs_helper = \is_target, queue, seen, graph ->
+bfs_helper = |is_target, queue, seen, graph|
when queue is
[] ->
- Err NotFound
+ Err(NotFound)
[current, ..] ->
- rest = List.dropFirst queue 1
+ rest = List.drop_first(queue, 1)
- if is_target current then
- Ok current
+ if is_target(current) then
+ Ok(current)
else
- when Dict.get graph current is
- Ok neighbors ->
+ when Dict.get(graph, current) is
+ Ok(neighbors) ->
# filter out all seen neighbors
- filtered = List.keepIf neighbors (\n -> !(Set.contains seen n))
+ filtered = List.keep_if(neighbors, |n| !(Set.contains(seen, n)))
# newly explored nodes are added to the FIFO queue
- new_queue = List.concat rest filtered
+ new_queue = List.concat(rest, filtered)
# the new nodes are also added to the seen set
- new_seen = List.walk filtered seen Set.insert
+ new_seen = List.walk(filtered, seen, Set.insert)
- bfs_helper is_target new_queue new_seen graph
+ bfs_helper(is_target, new_queue, new_seen, graph)
- Err KeyNotFound ->
- bfs_helper is_target rest seen graph
+ Err(KeyNotFound) ->
+ bfs_helper(is_target, rest, seen, graph)
# Test DFS with multiple paths
expect
- actual = dfs (\v -> Str.startsWith v "C") "A" test_graph_multipath
- expected = Ok "Ccorrect"
+ actual = dfs(|v| Str.starts_with(v, "C"), "A", test_graph_multipath)
+ expected = Ok("Ccorrect")
actual == expected
# Test BFS with multiple paths
expect
- actual = bfs (\v -> Str.startsWith v "C") "A" test_graph_multipath
- expected = Ok "Ccorrect"
+ actual = bfs(|v| Str.starts_with(v, "C"), "A", test_graph_multipath)
+ expected = Ok("Ccorrect")
actual == expected
# Test DFS
expect
- actual = dfs (\v -> Str.startsWith v "F") "A" test_graph_small
- expected = Ok "F-DFS"
+ actual = dfs(|v| Str.starts_with(v, "F"), "A", test_graph_small)
+ expected = Ok("F-DFS")
actual == expected
## Test BFS
expect
- actual = bfs (\v -> Str.startsWith v "F") "A" test_graph_small
- expected = Ok "F-BFS"
+ actual = bfs(|v| Str.starts_with(v, "F"), "A", test_graph_small)
+ expected = Ok("F-BFS")
actual == expected
# Test NotFound DFS
expect
- actual = dfs (\v -> v == "not a node") "A" test_graph_small
- expected = Err NotFound
+ actual = dfs(|v| v == "not a node", "A", test_graph_small)
+ expected = Err(NotFound)
actual == expected
# Test NotFound BFS
expect
- actual = dfs (\v -> v == "not a node") "A" test_graph_small
- expected = Err NotFound
+ actual = dfs(|v| v == "not a node", "A", test_graph_small)
+ expected = Err(NotFound)
actual == expected
# Test DFS large
expect
- actual = dfs (\v -> v == "AE") "A" test_graph_large
- expected = Ok "AE"
+ actual = dfs(|v| v == "AE", "A", test_graph_large)
+ expected = Ok("AE")
actual == expected
## Test BFS large
expect
- actual = bfs (\v -> v == "AE") "A" test_graph_large
- expected = Ok "AE"
+ actual = bfs(|v| v == "AE", "A", test_graph_large)
+ expected = Ok("AE")
actual == expected
diff --git a/examples/GraphTraversal/README.md b/examples/GraphTraversal/README.md
index 9daa2aa..84018de 100644
--- a/examples/GraphTraversal/README.md
+++ b/examples/GraphTraversal/README.md
@@ -14,5 +14,5 @@ Run this from the directory that has `Graph.roc` in it:
```
$ roc test Graph.roc
-0 failed and 4 passed in 653 ms.
+0 failed and 8 passed in 200 ms.
```
diff --git a/examples/HelloWeb/main.roc b/examples/HelloWeb/main.roc
index 3b3f351..17ac866 100644
--- a/examples/HelloWeb/main.roc
+++ b/examples/HelloWeb/main.roc
@@ -1,28 +1,30 @@
-app [Model, init!, respond!] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.11.0/yWHkcVUt_WydE1VswxKFmKFM5Tlu9uMn6ctPVYaas7I.tar.br" }
+app [Model, init!, respond!] { web: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.12.0/Q4h_In-sz1BqAvlpmCsBHhEJnn_YvfRRMiNACB_fBbk.tar.br" }
-import pf.Stdout
-import pf.Http exposing [Request, Response]
-import pf.Utc
+import web.Stdout
+import web.Http exposing [Request, Response]
+import web.Utc
# Model is produced by `init`.
Model : {}
# With `init` you can set up a database connection once at server startup,
# generate css by running `tailwindcss`,...
-# In this case we don't have anything to initialize, so it is just `Task.ok {}`.
+# In this case we don't have anything to initialize, so it is just `Ok({})`.
init! : {} => Result Model []
-init! = \_ -> Ok {}
+init! = |_| Ok({})
respond! : Request, Model => Result Response [ServerErr Str]_
-respond! = \req, _ ->
+respond! = |req, _|
# Log request datetime, method and url
- datetime = Utc.to_iso_8601 (Utc.now! {})
+ datetime = Utc.to_iso_8601(Utc.now!({}))
- try Stdout.line! "$(datetime) $(Inspect.toStr req.method) $(req.uri)"
+ Stdout.line!("${datetime} ${Inspect.to_str(req.method)} ${req.uri}")?
- Ok {
- status: 200,
- headers: [],
- body: Str.toUtf8 "Hello, web!",
- }
+ Ok(
+ {
+ status: 200,
+ headers: [],
+ body: Str.to_utf8("Hello, web!"),
+ },
+ )
diff --git a/examples/HelloWorld/main.roc b/examples/HelloWorld/main.roc
index 66edde3..07eaac8 100644
--- a/examples/HelloWorld/main.roc
+++ b/examples/HelloWorld/main.roc
@@ -1,6 +1,6 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
-import pf.Stdout
+import cli.Stdout
-main! = \_args ->
- Stdout.line! "Hello, World!"
+main! = |_args|
+ Stdout.line!("Hello, World!")
diff --git a/examples/ImportFromDirectory/Dir/Hello.roc b/examples/ImportFromDirectory/Dir/Hello.roc
index fae9d72..1ee54dd 100644
--- a/examples/ImportFromDirectory/Dir/Hello.roc
+++ b/examples/ImportFromDirectory/Dir/Hello.roc
@@ -2,5 +2,5 @@
module [hello]
hello : Str -> Str
-hello = \name ->
- "Hello $(name) from inside Dir!"
+hello = |name|
+ "Hello ${name} from inside Dir!"
diff --git a/examples/ImportFromDirectory/main.roc b/examples/ImportFromDirectory/main.roc
index cce91ee..74ac807 100644
--- a/examples/ImportFromDirectory/main.roc
+++ b/examples/ImportFromDirectory/main.roc
@@ -1,8 +1,8 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
-import pf.Stdout
+import cli.Stdout
import Dir.Hello exposing [hello]
-main! = \_args ->
+main! = |_args|
# here we're calling the `hello` function from the Hello module
- Stdout.line! (hello "World")
+ Stdout.line!(hello("World"))
diff --git a/examples/ImportPackageFromModule/Module.roc b/examples/ImportPackageFromModule/Module.roc
index bb62ffa..f59b488 100644
--- a/examples/ImportPackageFromModule/Module.roc
+++ b/examples/ImportPackageFromModule/Module.roc
@@ -2,4 +2,4 @@ module [split_graphemes]
import unicode.Grapheme
-split_graphemes = \string -> Grapheme.split string
+split_graphemes = |string| Grapheme.split(string)
diff --git a/examples/ImportPackageFromModule/main.roc b/examples/ImportPackageFromModule/main.roc
index 038fcbe..e5635c7 100644
--- a/examples/ImportPackageFromModule/main.roc
+++ b/examples/ImportPackageFromModule/main.roc
@@ -1,12 +1,14 @@
### start snippet header
app [main!] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
- unicode: "https://github.com/roc-lang/unicode/releases/download/0.1.2/vH5iqn04ShmqP-pNemgF773f86COePSqMWHzVGrAKNo.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
+ unicode: "https://github.com/roc-lang/unicode/releases/download/0.3.0/9KKFsA4CdOz0JIOL7iBSI_2jGIXQ6TsFBXgd086idpY.tar.br",
}
### end snippet header
-import pf.Stdout
+import cli.Stdout
import Module
-main! = \_args ->
- Stdout.line! (Inspect.toStr (Module.split_graphemes "hello"))
+main! = |_args|
+ Module.split_graphemes("hello")
+ |> Inspect.to_str
+ |> Stdout.line!
diff --git a/examples/IngestFiles/README.md b/examples/IngestFiles/README.md
index de307d1..8b1bec3 100644
--- a/examples/IngestFiles/README.md
+++ b/examples/IngestFiles/README.md
@@ -1,10 +1,10 @@
# Ingest Files
-Statically importing files as a `Str` or a `List U8` (list of bytes):
+To statically import files as a `Str` or a `List U8` (list of bytes):
```roc
-import "some-file" as someStr : Str
-import "some-file" as someBytes : List U8
+import "some-file" as some_str : Str
+import "some-file" as some_bytes : List U8
```
## Code
diff --git a/examples/IngestFiles/main.roc b/examples/IngestFiles/main.roc
index f78c382..2aecb3d 100644
--- a/examples/IngestFiles/main.roc
+++ b/examples/IngestFiles/main.roc
@@ -1,7 +1,7 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
-import pf.Stdout
+import cli.Stdout
import "sample.txt" as sample : Str
-main! = \_args ->
- Stdout.line! "$(sample)"
+main! = |_args|
+ Stdout.line!("${sample}")
diff --git a/examples/Json/README.md b/examples/Json/README.md
index 47b5df8..6e03962 100644
--- a/examples/Json/README.md
+++ b/examples/Json/README.md
@@ -1,6 +1,6 @@
# JSON
-Decode JSON data.
+Decode JSON data into a Roc record.
## Code
```roc
diff --git a/examples/Json/main.roc b/examples/Json/main.roc
index 0200ae2..91894c8 100644
--- a/examples/Json/main.roc
+++ b/examples/Json/main.roc
@@ -1,26 +1,25 @@
app [main!] {
- cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
- json: "https://github.com/lukewilliamboswell/roc-json/releases/download/0.11.0/z45Wzc-J39TLNweQUoLw3IGZtkQiEN3lTBv3BXErRjQ.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
+ json: "https://github.com/lukewilliamboswell/roc-json/releases/download/0.12.0/1trwx8sltQ-e9Y2rOB4LWUWLS_sFVyETK8Twl0i9qpw.tar.gz",
}
import cli.Stdout
import json.Json
-main! = \_args ->
- request_body = Str.toUtf8 "{\"Image\":{\"Animated\":false,\"Height\":600,\"Ids\":[116,943,234,38793],\"Thumbnail\":{\"Height\":125,\"Url\":\"http:\\/\\/www.example.com\\/image\\/481989943\",\"Width\":100},\"Title\":\"View from 15th Floor\",\"Width\":800}}"
+main! = |_args|
+ request_body = Str.to_utf8("{\"Image\":{\"Animated\":false,\"Height\":600,\"Ids\":[116,943,234,38793],\"Thumbnail\":{\"Height\":125,\"Url\":\"http:\\/\\/www.example.com\\/image\\/481989943\",\"Width\":100},\"Title\":\"View from 15th Floor\",\"Width\":800}}")
- # This { fieldNameMapping: PascalCase } setting translates
+ # This { field_name_mapping: PascalCase } setting translates
# incoming JSON fields from PascalCase (first letter capitalized)
- # to camelCase (first letter uncapitalized), which is what
- # Roc field names always use.
- decoder = Json.utf8With { fieldNameMapping: PascalCase }
+ # to snake_case, which is what Roc field names always use.
+ decoder = Json.utf8_with({ field_name_mapping: PascalCase })
decoded : DecodeResult ImageRequest
- decoded = Decode.fromBytesPartial request_body decoder
+ decoded = Decode.from_bytes_partial(request_body, decoder)
when decoded.result is
- Ok record -> Stdout.line! "Successfully decoded image, title:\"$(record.image.title)\""
- Err _ -> Err (Exit 1 "Error, failed to decode image")
+ Ok(record) -> Stdout.line!("Successfully decoded image, title:\"${record.image.title}\"")
+ Err(_) -> Err(Exit(1, "Error, failed to decode image"))
ImageRequest : {
image : {
diff --git a/examples/LeastSquares/main.roc b/examples/LeastSquares/main.roc
index 1ed6113..1276f61 100644
--- a/examples/LeastSquares/main.roc
+++ b/examples/LeastSquares/main.roc
@@ -1,29 +1,25 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
-import pf.Stdout
+import cli.Stdout
-main! = \_args ->
- nStr = Num.toStr (least_square_difference {})
+main! = |_args|
+ n_str = Num.to_str(least_square_difference)
- Stdout.line! "The least positive integer n, where the difference of n*n and (n-1)*(n-1) is greater than 1000, is $(nStr)"
+ Stdout.line!("The least positive integer n, where the difference of n*n and (n-1)*(n-1) is greater than 1000, is ${n_str}")
-## A recursive function that takes an `U32` as its input and returns the least
-## positive integer number `n`, where the difference of `n*n` and `(n-1)*(n-1)`
-## is greater than 1000.
+## The smallest positive integer number `n`, where the difference
+## of `n*n` and `(n-1)*(n-1)` is greater than 1000.
##
-## The input `n` should be a positive integer, and the function will return an
-## `U32`representing the least positive integer that satisfies the condition.
-##
-least_square_difference : {} -> U32
-least_square_difference = \_ ->
- find_number = \n ->
- difference = (Num.powInt n 2) - (Num.powInt (n - 1) 2)
+least_square_difference : U32
+least_square_difference =
+ find_number = |n|
+ difference = (Num.pow_int(n, 2)) - (Num.pow_int((n - 1), 2))
if difference > 1000 then
n
else
- find_number (n + 1)
+ find_number((n + 1))
- find_number 1
+ find_number(1)
-expect least_square_difference {} == 501
+expect least_square_difference == 501
diff --git a/examples/LoopEffect/README.md b/examples/LoopEffect/README.md
index cf527cf..62c88b4 100644
--- a/examples/LoopEffect/README.md
+++ b/examples/LoopEffect/README.md
@@ -1,5 +1,5 @@
# Loop Effects
-Sometimes, you need to repeat an [effectful](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) function, multiple times until a particular event occurs. In roc, you can use a [recursive function](https://en.wikipedia.org/wiki/Recursion_(computer_science)) to do this.
+Sometimes, you need to repeat an [effectful](https://en.wikipedia.org/wiki/Side_effect_(computer_science)) function, multiple times until a particular event occurs. In Roc, you can use a [recursive function](https://en.wikipedia.org/wiki/Recursion_(computer_science)) to do this.
We'll demonstrate this by adding numbers read from stdin until the end of input (Ctrl-D or [end of file](https://en.wikipedia.org/wiki/End-of-file)).
diff --git a/examples/LoopEffect/main.roc b/examples/LoopEffect/main.roc
index 754b43d..5aa2718 100644
--- a/examples/LoopEffect/main.roc
+++ b/examples/LoopEffect/main.roc
@@ -1,37 +1,38 @@
app [main!] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
}
-import pf.Stdin
-import pf.Stdout
-import pf.Stderr
+import cli.Stdin
+import cli.Stdout
+import cli.Stderr
-main! = \_args ->
- when run! {} is
- Ok {} -> Ok {}
- Err err -> print_err! err
+main! = |_args|
+ when run!({}) is
+ Ok({}) -> Ok({})
+ Err(err) -> print_err!(err)
run! : {} => Result {} _
-run! = \_ ->
- try Stdout.line! "Enter some numbers on different lines, then press Ctrl-D to sum them up."
+run! = |_|
+ Stdout.line!("Enter some numbers on different lines, then press Ctrl-D to sum them up.")?
- sum = try add_number_from_stdin! 0
+ sum = add_number_from_stdin!(0)?
- Stdout.line! "Sum: $(Num.toStr sum)"
+ Stdout.line!("Sum: ${Num.to_str(sum)}")
+## recursive function that sums every number that is provided through stdin
add_number_from_stdin! : I64 => Result I64 _
-add_number_from_stdin! = \sum ->
- when Stdin.line! {} is
- Ok input ->
- when Str.toI64 input is
- Ok num -> add_number_from_stdin! (sum + num)
- Err _ -> Err (NotNum input)
+add_number_from_stdin! = |sum|
+ when Stdin.line!({}) is
+ Ok(input) ->
+ when Str.to_i64(input) is
+ Ok(num) -> add_number_from_stdin!((sum + num))
+ Err(_) -> Err(NotNum(input))
- Err EndOfFile -> Ok sum
- Err err -> err |> Inspect.toStr |> NotNum |> Err
+ Err(EndOfFile) -> Ok(sum)
+ Err(err) -> err |> Inspect.to_str |> NotNum |> Err
print_err! : _ => Result {} _
-print_err! = \err ->
+print_err! = |err|
when err is
- NotNum text -> Stderr.line! "Error: \"$(text)\" is not a valid I64 number."
- _ -> Stderr.line! "Error: $(Inspect.toStr err)"
+ NotNum(text) -> Stderr.line!("Error: \"${text}\" is not a valid I64 number.")
+ _ -> Stderr.line!("Error: ${Inspect.to_str(err)}")
diff --git a/examples/MultiLineComments/README.md b/examples/MultiLineComments/README.md
index d4bbd17..1b77290 100644
--- a/examples/MultiLineComments/README.md
+++ b/examples/MultiLineComments/README.md
@@ -1,6 +1,6 @@
# Multi-line comments
-Roc does not support native multi-line comments like `/*...*/` in other languages.
+Roc does not support native multi-line comments like `/*...*/` in other languages (see below for why).
It's `#` all the way:
```roc
diff --git a/examples/MultipleRocFiles/Hello.roc b/examples/MultipleRocFiles/Hello.roc
index 602ab0a..1c3d48e 100644
--- a/examples/MultipleRocFiles/Hello.roc
+++ b/examples/MultipleRocFiles/Hello.roc
@@ -2,5 +2,5 @@
module [hello]
hello : Str -> Str
-hello = \name ->
- "Hello $(name) from interface!"
+hello = |name|
+ "Hello ${name} from module!"
diff --git a/examples/MultipleRocFiles/README.md b/examples/MultipleRocFiles/README.md
index 214a646..4f4e224 100644
--- a/examples/MultipleRocFiles/README.md
+++ b/examples/MultipleRocFiles/README.md
@@ -22,5 +22,5 @@ Run this from the directory that has `main.roc` in it:
```
$ roc main.roc
-Hello World from interface!
+Hello World from module!
```
diff --git a/examples/MultipleRocFiles/main.roc b/examples/MultipleRocFiles/main.roc
index f5923e2..0b22a25 100644
--- a/examples/MultipleRocFiles/main.roc
+++ b/examples/MultipleRocFiles/main.roc
@@ -1,7 +1,7 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
-import pf.Stdout
+import cli.Stdout
import Hello
-main! = \_args ->
- Stdout.line! (Hello.hello "World")
+main! = |_args|
+ Stdout.line!(Hello.hello("World"))
diff --git a/examples/Parser/main.roc b/examples/Parser/main.roc
index 2d4690e..1059cf3 100644
--- a/examples/Parser/main.roc
+++ b/examples/Parser/main.roc
@@ -1,57 +1,56 @@
app [main!] {
- cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
- parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.9.0/w8YKp2YAgQt5REYk912HfKAHBjcXsrnvtjI0CBzoAT4.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
+ parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.10.0/6eZYaXkrakq9fJ4oUc0VfdxU1Fap2iTuAN18q9OgQss.tar.br",
}
import cli.Stdout
-import parser.Parser exposing [Parser, many, oneOf, map]
-import parser.String exposing [parseStr, codeunit, anyCodeunit]
+import parser.Parser exposing [Parser, many, one_of, map]
+import parser.String exposing [parse_str, codeunit, any_codeunit]
-main! = \_args ->
+main! = |_args|
- letters = try parseStr (many letter_parser) input_str
+ letters = parse_str(many(letter_parser), input_str)?
msg =
letters
|> count_letter_a
- |> \count -> "I counted $(count) letter A's!"
+ |> |count| "I counted ${count} letter A's!"
- Stdout.line! msg
+ Stdout.line!(msg)
Letter : [A, B, C, Other]
input_str = "AAAiBByAABBwBtCCCiAyArBBx"
-# Helper to check if a letter is an A tag
-isA = \l -> l == A
-
# Count the number of Letter A's
count_letter_a : List Letter -> Str
-count_letter_a = \letters ->
+count_letter_a = |letters|
letters
- |> List.countIf isA
- |> Num.toStr
+ |> List.count_if(|l| l == A)
+ |> Num.to_str
-# Parser to convert utf8 input into Letter tags
+# Parser to convert utf8 input into Letter [tags](https://www.roc-lang.org/tutorial#tags)
letter_parser : Parser (List U8) Letter
letter_parser =
- oneOf [
- codeunit 'A' |> map \_ -> A,
- codeunit 'B' |> map \_ -> B,
- codeunit 'C' |> map \_ -> C,
- anyCodeunit |> map \_ -> Other,
- ]
-
-# Test we can parse a single B letter
+ one_of(
+ [
+ codeunit('A') |> map(|_| A),
+ codeunit('B') |> map(|_| B),
+ codeunit('C') |> map(|_| C),
+ any_codeunit |> map(|_| Other),
+ ],
+ )
+
+# Test parsing a single letter B
expect
input = "B"
parser = letter_parser
- result = parseStr parser input
- result == Ok B
+ result = parse_str(parser, input)
+ result == Ok(B)
-# Test we can parse a number of different letters
+# Test parsing a number of different letters
expect
input = "BCXA"
- parser = many letter_parser
- result = parseStr parser input
- result == Ok [B, C, Other, A]
+ parser = many(letter_parser)
+ result = parse_str(parser, input)
+ result == Ok([B, C, Other, A])
diff --git a/examples/PatternMatching/PatternMatching.roc b/examples/PatternMatching/PatternMatching.roc
index c225148..967de4a 100644
--- a/examples/PatternMatching/PatternMatching.roc
+++ b/examples/PatternMatching/PatternMatching.roc
@@ -2,88 +2,108 @@ module []
# Match an empty list
expect
- match = \input ->
+ match = |input|
when input is
[] -> EmptyList
_ -> Other
- (match [] == EmptyList)
- && (match [A, B, C] != EmptyList)
+ match([])
+ == EmptyList
+ and match([A, B, C])
+ != EmptyList
# Match a non-empty list
expect
- match = \input ->
+ match = |input|
when input is
[_, ..] -> NonEmptyList
_ -> Other
- (match [A, B, C] == NonEmptyList)
- && (match [] != NonEmptyList)
+ match([A, B, C])
+ == NonEmptyList
+ and match([])
+ != NonEmptyList
# Match a list whose first element is the string "Hi"
expect
- match = \input ->
+ match = |input|
when input is
["Hi", ..] -> StartsWithHi
_ -> Other
- (match ["Hi", "Hello", "Yo"] == StartsWithHi)
- && (match ["Hello", "Yo", "Hi"] != StartsWithHi)
+ match(["Hi", "Hello", "Yo"])
+ == StartsWithHi
+ and match(["Hello", "Yo", "Hi"])
+ != StartsWithHi
# Match a list whose last element is the number 42
expect
- match = \input ->
+ match = |input|
when input is
[.., 42] -> EndsWith42
_ -> Other
- (match [24, 64, 42] == EndsWith42)
- && (match [42, 1, 5] != EndsWith42)
+ match([24, 64, 42])
+ == EndsWith42
+ and match([42, 1, 5])
+ != EndsWith42
# Match a list that starts with a Foo tag
# followed by a Bar tag
expect
- match = \input ->
+ match = |input|
when input is
[Foo, Bar, ..] -> StartsWithFooBar
_ -> Other
- (match [Foo, Bar, Bar] == StartsWithFooBar)
- && (match [Bar, Bar, Foo] != StartsWithFooBar)
+ match([Foo, Bar, Bar])
+ == StartsWithFooBar
+ and match([Bar, Bar, Foo])
+ != StartsWithFooBar
# Match a list with these exact elements:
# Foo, Bar, and then (Baz "Hi")
expect
- match = \input ->
+ match = |input|
when input is
- [Foo, Bar, Baz "Hi"] -> FooBarBazStr
+ [Foo, Bar, Baz("Hi")] -> FooBarBazStr
_ -> Other
- (match [Foo, Bar, Baz "Hi"] == FooBarBazStr)
- && (match [Foo, Bar] != FooBarBazStr)
- && (match [Foo, Bar, Baz "Hi", Blah] != FooBarBazStr)
+ match([Foo, Bar, Baz("Hi")])
+ == FooBarBazStr
+ and match([Foo, Bar])
+ != FooBarBazStr
+ and match([Foo, Bar, Baz("Hi"), Blah])
+ != FooBarBazStr
# Match a list with Foo as its first element, and
# Count for its second element. Count holds a number,
# and we only match if that number is greater than 0.
expect
- match = \input ->
+ match = |input|
when input is
- [Foo, Count num, ..] if num > 0 -> FooCountIf
+ [Foo, Count(num), ..] if num > 0 -> FooCountIf
_ -> Other
- (match [Foo, Count 1] == FooCountIf)
- && (match [Foo, Count 0] != FooCountIf)
- && (match [Baz, Count 1] != FooCountIf)
+ match([Foo, Count(1)])
+ == FooCountIf
+ and match([Foo, Count(0)])
+ != FooCountIf
+ and match([Baz, Count(1)])
+ != FooCountIf
# Use `as` to create a variable equal to the part of the list that matches `..`
expect
- match = \input ->
+ match = |input|
when input is
- [head, .. as tail] -> HeadAndTail head tail
+ [head, .. as tail] -> HeadAndTail(head, tail)
_ -> Other
- (match [1, 2, 3] == HeadAndTail 1 [2, 3])
- && (match [1, 2] == HeadAndTail 1 [2])
- && (match [1] == HeadAndTail 1 [])
- && (match [] == Other)
+ match([1, 2, 3])
+ == HeadAndTail(1, [2, 3])
+ and match([1, 2])
+ == HeadAndTail(1, [2])
+ and match([1])
+ == HeadAndTail(1, [])
+ and match([])
+ == Other
diff --git a/examples/PatternMatching/README.md b/examples/PatternMatching/README.md
index b550d40..ddd27bb 100644
--- a/examples/PatternMatching/README.md
+++ b/examples/PatternMatching/README.md
@@ -13,9 +13,9 @@ when input is
[Foo, Bar, ..] -> StartsWithFooBar
- [Foo, Bar, Baz "Hi"] -> FooBarBazStr
+ [Foo, Bar, Baz("Hi")] -> FooBarBazStr
- [Foo, Count num, ..] if num > 0 -> FooCountIf
+ [Foo, Count(num), ..] if num > 0 -> FooCountIf
[head, .. as tail] -> HeadAndTail head tail
diff --git a/examples/RandomNumbers/README.md b/examples/RandomNumbers/README.md
index e5b4ef9..86ffc05 100644
--- a/examples/RandomNumbers/README.md
+++ b/examples/RandomNumbers/README.md
@@ -4,9 +4,15 @@ Generate a list of random numbers using the [roc-random package](https://github.
## Random `Generators`
-Some languages provide a function like JavaScript’s `Math.random()` which can return a different number each time you call it. However, functions in Roc are guaranteed to return the same answer when given the same arguments, so something like `Math.random` couldn’t possibly be a valid Roc function! As such, we use a different approach to generate random numbers in Roc.
-
-This example uses a `Generator` which generates pseudorandom numbers using an initial seed value and the [PCG algorithm](https://www.pcg-random.org/). Note that if the same seed is provided, then the same number sequence will be generated every time! The appearance of randomness comes entirely from deterministic math being done on that initial seed. (The same is true of `Math.random()`, except that `Math.random()` silently chooses a seed for you at runtime.)
+Some languages provide a function like JavaScript’s `Math.random()` which can return a different number each time you call it.
+However, functions in Roc are guaranteed to return the same answer when given the same arguments, this has many benefits.
+But this also means something like `Math.random` couldn’t possibly be a valid Roc function!
+So, we use a different approach to generate random numbers in Roc.
+
+This example uses a `Generator` which generates pseudorandom numbers using an initial seed value and the [PCG algorithm](https://www.pcg-random.org/).
+If the same seed is provided, then the same number sequence will be generated every time!
+The appearance of randomness comes entirely from deterministic math being done on that initial seed.
+The same is true of `Math.random()`, except that `Math.random()` silently chooses a seed for you at runtime.
## Code
```roc
diff --git a/examples/RandomNumbers/main.roc b/examples/RandomNumbers/main.roc
index 6608d7f..f8a1a9d 100644
--- a/examples/RandomNumbers/main.roc
+++ b/examples/RandomNumbers/main.roc
@@ -1,35 +1,36 @@
app [main!] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
- rand: "https://github.com/lukewilliamboswell/roc-random/releases/download/0.4.0/Ai2KfHOqOYXZmwdHX3g3ytbOUjTmZQmy0G2R9NuPBP0.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
+ rand: "https://github.com/lukewilliamboswell/roc-random/releases/download/0.5.0/yDUoWipuyNeJ-euaij4w_ozQCWtxCsywj68H0PlJAdE.tar.br",
}
-import pf.Stdout
+import cli.Stdout
import rand.Random
-main! = \_args ->
+main! = |_args|
- # Print a list of 10 random numbers in the range 25-75 inclusive.
+ # Print a list of 10 random numbers.
numbers_str =
- randomNumbers
- |> List.map Num.toStr
- |> Str.joinWith "\n"
+ random_numbers
+ |> List.map(Num.to_str)
+ |> Str.join_with("\n")
- Stdout.line! numbers_str
+ Stdout.line!(numbers_str)
-# Generate a list random numbers using the seed `1234`.
+# Generate a list of random numbers using the seed `1234`.
# This is NOT cryptograhpically secure!
-randomNumbers : List U32
-randomNumbers =
- { value: numbers } = Random.step (Random.seed 1234) numbersGenerator
+random_numbers : List U32
+random_numbers =
+ { value: numbers } = Random.step(Random.seed(1234), numbers_generator)
numbers
-# A generator that will produce a list of 10 random numbers in the range 25-75 inclusive.
+# A generator that will produce a list of 10 random numbers in the range 25-75.
+# This includes the boundaries, so the numbers can be 25 or 75.
# This is NOT cryptograhpically secure!
-numbersGenerator : Random.Generator (List U32)
-numbersGenerator =
- Random.list (Random.boundedU32 25 75) 10
+numbers_generator : Random.Generator (List U32)
+numbers_generator =
+ Random.list(Random.bounded_u32(25, 75), 10)
expect
- actual = randomNumbers
+ actual = random_numbers
actual == [52, 34, 26, 69, 34, 35, 51, 74, 70, 39]
diff --git a/examples/RecordBuilder/DateParser.roc b/examples/RecordBuilder/DateParser.roc
index 7790ce3..b883aac 100644
--- a/examples/RecordBuilder/DateParser.roc
+++ b/examples/RecordBuilder/DateParser.roc
@@ -6,44 +6,63 @@ module [
build_segment_parser,
]
+# see README.md for explanation of code
ParserErr : [InvalidNumStr, OutOfSegments]
ParserGroup a := List Str -> Result (a, List Str) ParserErr
parse_with : (Str -> Result a ParserErr) -> ParserGroup a
-parse_with = \parser ->
- @ParserGroup \segments ->
- when segments is
- [] -> Err OutOfSegments
- [first, .. as rest] ->
- parsed = parser? first
- Ok (parsed, rest)
+parse_with = |parser|
+ @ParserGroup(
+ |segments|
+ when segments is
+ [] -> Err(OutOfSegments)
+ [first, .. as rest] ->
+ parsed = parser(first)?
+ Ok((parsed, rest)),
+ )
chain_parsers : ParserGroup a, ParserGroup b, (a, b -> c) -> ParserGroup c
-chain_parsers = \@ParserGroup first, @ParserGroup second, combiner ->
- @ParserGroup \segments ->
- (a, after_first) = first? segments
- (b, after_second) = second? after_first
+chain_parsers = |@ParserGroup(first), @ParserGroup(second), combiner|
+ @ParserGroup(
+ |segments|
+ (a, after_first) = first(segments)?
+ (b, after_second) = second(after_first)?
- Ok (combiner a b, after_second)
+ Ok((combiner(a, b), after_second)),
+ )
build_segment_parser : ParserGroup a -> (Str -> Result a ParserErr)
-build_segment_parser = \@ParserGroup parser_group ->
- \text ->
- segments = Str.splitOn text "-"
- (date, _remaining) = parser_group? segments
+build_segment_parser = |@ParserGroup(parser_group)|
+ |text|
+ segments = Str.split_on(text, "-")
+ (date, _remaining) = parser_group(segments)?
- Ok date
+ Ok(date)
expect
date_parser =
{ chain_parsers <-
- month: parse_with Ok,
- day: parse_with Str.toU64,
- year: parse_with Str.toU64,
+ month: parse_with(Ok),
+ day: parse_with(Str.to_u64),
+ year: parse_with(Str.to_u64),
}
|> build_segment_parser
- date = date_parser "Mar-10-2015"
+ date_parser("Mar-10-2015") == Ok({ month: "Mar", day: 10, year: 2015 })
- date == Ok { month: "Mar", day: 10, year: 2015 }
+expect
+ date_parser =
+ build_segment_parser(
+ chain_parsers(
+ parse_with(Ok),
+ chain_parsers(
+ parse_with(Str.to_u64),
+ parse_with(Str.to_u64),
+ |day, year| (day, year),
+ ),
+ |month, (day, year)| { month, day, year },
+ ),
+ )
+
+ date_parser("Mar-10-2015") == Ok({ month: "Mar", day: 10, year: 2015 })
diff --git a/examples/RecordBuilder/README.md b/examples/RecordBuilder/README.md
index 60efb53..0688e10 100644
--- a/examples/RecordBuilder/README.md
+++ b/examples/RecordBuilder/README.md
@@ -1,12 +1,15 @@
-# Record Builder
+# Record Builder
-Record builders are a syntax sugar for sequencing actions and collecting the intermediate results as fields in a record. All you need to build a record is a `map2`-style function that takes two values of the same type and combines them using a provided combiner fnuction. There are many convenient APIs we can build with this simple syntax.
+Record builders are a syntax sugar for sequencing actions and collecting the intermediate results as fields in a record.
+All you need to build a record is a `map2`-style function that takes two values of the same type and combines them using a provided combiner fnuction. There are many convenient APIs we can build with this simple syntax.
## The Basics
-Let's assume we want to develop a module that parses any text with segments delimited by dashes. The record builder pattern can help us here to parse each segment in their own way, and short circuit on the first failure.
+Let's assume we want to develop a module that parses any text with segments delimited by dashes, like "Mar-10-2015".
+The record builder pattern can help us here to parse each segment in their own way, and [short circuit](https://en.wikipedia.org/wiki/Short-circuit_evaluation) on the first failure.
-> Note: it is possible to parse dash-delimited text in a specific format with simpler code. However, generic APIs built with record builders can be much simpler and readable than any such specific implementation.
+> Note: it is possible to parse dash-delimited text in a specific format with simpler code.
+However, generic APIs built with record builders can be much simpler and readable.
## Defining Types
@@ -14,7 +17,9 @@ Let's assume we want to develop a module that parses any text with segments deli
ParserGroup a := List Str -> Result (a, List Str) ParserErr
```
-We start by defining a `ParserGroup`, which is a [parser combinator](https://en.wikipedia.org/wiki/Parser_combinator) that takes in a list of string segments to parse, and returns parsed data as well as the remaining, unparsed segments. All of the parsers that render to our builder's fields are `ParserGroup` values, and get chained together into one big `ParserGroup`.
+We start by defining a `ParserGroup`, which is a [parser combinator](https://en.wikipedia.org/wiki/Parser_combinator)
+that takes a list of string segments to parse, and returns parsed data, as well as the remaining, unparsed segments.
+All of the parsers that render to our builder's fields are `ParserGroup` values, and get chained together into one big `ParserGroup`.
You'll notice that record builders all tend to deal with a single wrapping type, as we can only combine said values with our `map2`-style function if all combined values are the same type. On the plus side, this allows record builders to work with a single value, two fields, or ten, allowing for great composability.
@@ -24,83 +29,90 @@ It's useful to visualize our desired result. The record builder pattern we're ai
```roc
expect
- dateParser : ParserGroup Date
- dateParser =
- { chainParsers <-
- month: parseWith Ok,
- day: parseWith Str.toU64,
- year: parseWith Str.toU64,
+ date_parser =
+ { chain_parsers <-
+ month: parse_with(Ok),
+ day: parse_with(Str.to_u64),
+ year: parse_with(Str.to_u64),
}
- |> buildSegmentParser
+ |> build_segment_parser
- date = dateParser "Mar-10-2015"
-
- date == Ok { month: "Mar", day: 10, year: 2015 }
+ date_parser("Mar-10-2015") == Ok({ month: "Mar", day: 10, year: 2015 })
```
-This generates a record with fields `month`, `day`, and `year`, all possessing specific parts of the provided date. Note the slight deviation from the conventional record syntax, with the `chainParsers <-` at the top, which is our `map2`-style function.
+This generates a record with fields `month`, `day`, and `year`, all possessing specific parts of the provided date.
+Note the slight deviation from the conventional record syntax, with the `chain_parsers <-` at the top, which is our `map2`-style function.
## Under the Hood
-The record builder pattern is syntax sugar which converts the preceding into:
+The record builder pattern is [syntax sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) which converts the previous code block into:
```roc
expect
- dateParser : ParserGroup Date
- dateParser =
- chainParsers
- (parseWith Ok)
- (chainParsers
- (parseWith Str.toU64)
- (parseWith Str.toU64)
- (\day, year -> (day, year))
- )
- (\month, (day, year) -> { month, day, year })
+ date_parser =
+ build_segment_parser(chain_parsers(
+ parse_with(Ok),
+ chain_parsers(
+ parse_with(Str.to_u64),
+ parse_with(Str.to_u64),
+ |day, year| (day, year),
+ ),
+ |month, (day, year)| { month, day, year },
+ ))
+
+ date_parser("Mar-10-2015") == Ok({ month: "Mar", day: 10, year: 2015 })
```
In short, we chain together all pairs of field values with the `map2` combining function, pairing them into tuples until the final grouping of values is structured as a record.
-To make the above possible, we'll need to define the `parseWith` function that turns a parser into a `ParserGroup`, and the `chainParsers` function that acts as our `map2` combining function.
+To make the above possible, we'll need to define the `parse_with` function that turns a parser into a `ParserGroup`, and the `chain_parsers` function that acts as our `map2` combining function.
## Defining Our Functions
-Let's start with `parseWith`:
+Let's start with `parse_with`:
```roc
-parseWith : (Str -> Result a ParserErr) -> ParserGroup a
-parseWith = \parser ->
- @ParserGroup \segments ->
- when segments is
- [] -> Err OutOfSegments
- [first, .. as rest] ->
- parsed = parser? first
- Ok (parsed, rest)
+parse_with : (Str -> Result a ParserErr) -> ParserGroup a
+parse_with = |parser|
+ @ParserGroup(
+ |segments|
+ when segments is
+ [] -> Err(OutOfSegments)
+ [first, .. as rest] ->
+ parsed = parser(first)?
+ Ok((parsed, rest)),
+ )
```
-This parses the first segment available, and returns the parsed data along with all remaining segments not yet parsed. We could already use this to parse a single-segment string without even using a record builder, but that wouldn't be very useful. Let's see how our `chainParsers` function will manage combining two `ParserGroup`s in serial:
+This parses the first segment available, and returns the parsed data along with all remaining segments not yet parsed.
+We could already use this to parse a single-segment string without even using a record builder, but that wouldn't be very useful.
+Let's see how our `chain_parsers` function will manage combining two `ParserGroup`s in serial:
```roc
-chainParsers : ParserGroup a, ParserGroup b, (a, b -> c) -> ParserGroup c
-chainParsers = \@ParserGroup first, @ParserGroup second, combiner ->
- @ParserGroup \segments ->
- (a, afterFirst) = first? segments
- (b, afterSecond) = second? afterFirst
-
- Ok (combiner a b, afterSecond)
+chain_parsers : ParserGroup a, ParserGroup b, (a, b -> c) -> ParserGroup c
+chain_parsers = |@ParserGroup(first), @ParserGroup(second), combiner|
+ @ParserGroup(
+ |segments|
+ (a, after_first) = first(segments)?
+ (b, after_second) = second(after_first)?
+
+ Ok((combiner(a, b), after_second)),
+ )
```
-Just parse the two groups, and then combine their results? That was easy!
+We parse the two groups (see `first` and `second`), and then combine their results.
-Finally, we'll need to wrap up our parsers into one that breaks a string into segments and then applies our parsers on said segments. We can call it `buildSegmentParser`:
+Finally, we'll need to wrap up our parsers into one that breaks a string into segments and then applies our parsers on those segments.
+We can call it `build_segment_parser`:
```roc
-buildSegmentParser : ParserGroup a -> (Str -> Result a ParserErr)
-buildSegmentParser = \@ParserGroup parserGroup ->
- \text ->
- segments = Str.splitOn text "-"
- (date, _remaining) = parserGroup? segments
+build_segment_parser : ParserGroup a -> (Str -> Result a ParserErr)
+build_segment_parser = |@ParserGroup(parser_group)|
+ |text|
+ segments = Str.split_on(text, "-")
+ (date, _remaining) = parser_group(segments)?
- Ok date
+ Ok(date)
```
Now we're ready to use our parser as much as we want on any input text!
@@ -116,7 +128,7 @@ file:DateParser.roc
Code for the above example is available in `DateParser.roc` which you can run like this:
```sh
-% roc test IDCounter.roc
+% roc test DateParser.roc
-0 failed and 1 passed in 190 ms.
+0 failed and 2 passed in 190 ms.
```
diff --git a/examples/Results/README.md b/examples/Results/README.md
index 3b42141..df6d119 100644
--- a/examples/Results/README.md
+++ b/examples/Results/README.md
@@ -1,8 +1,6 @@
# Results & Error Handling
-TODO update this example with a snippet using the `try` keyword, see issue #227
-
-This example shows how to use [`Result`](https://www.roc-lang.org/builtins/Result) in functions that can return errors. We will see how to use `Result.try` or the try operator `?` to chain functions and return the first error if any occurs.
+This example shows how to use [`Result`](https://www.roc-lang.org/builtins/Result) in functions that can return errors. We will see how to use `Result.try` or the `?` operator to chain functions and return the first error if any occurs.
## Code
diff --git a/examples/Results/main.roc b/examples/Results/main.roc
index 7d1a380..65882e4 100644
--- a/examples/Results/main.roc
+++ b/examples/Results/main.roc
@@ -1,105 +1,127 @@
app [main!] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
+ cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br",
}
-import pf.Stdout
+import cli.Stdout
+
+Person : { first_name : Str, last_name : Str, birth_year : U16 }
## This function parses strings like "{FirstName} {LastName} was born in {Year}"
-## and if successful returns `Ok {firstName, lastName, birthYear}`. Otherwise
+## and if successful returns `Ok {first_name, last_name, birth_year}`. Otherwise
## it returns an `Err` containing a descriptive tag.
## This is the most verbose version, we will do better below.
-parse_verbose = \line ->
- when line |> Str.splitFirst " was born in " is
- Ok { before: full_name, after: birth_year_str } ->
- when full_name |> Str.splitFirst " " is
- Ok { before: first_name, after: last_name } ->
- when Str.toU16 birth_year_str is
- Ok birth_year ->
- Ok { first_name, last_name, birth_year }
+parse_verbose : Str -> Result Person [InvalidRecordFormat, InvalidNameFormat, InvalidBirthYearFormat]
+parse_verbose = |line|
+ when line |> Str.split_first(" was born in ") is
+ Ok({ before: full_name, after: birth_year_str }) ->
+ when full_name |> Str.split_first(" ") is
+ Ok({ before: first_name, after: last_name }) ->
+ when Str.to_u16(birth_year_str) is
+ Ok(birth_year) ->
+ Ok({ first_name, last_name, birth_year })
- Err _ -> Err InvalidBirthYearFormat
+ Err(_) -> Err(InvalidBirthYearFormat)
- _ -> Err InvalidNameFormat
+ _ -> Err(InvalidNameFormat)
- _ -> Err InvalidRecordFormat
+ _ -> Err(InvalidRecordFormat)
## Here's a very slightly shorter version using `Result.try` to chain multiple
## functions that each could return an error. It's a bit nicer, don't you think?
-## Note: this version returns "raw" errors (`Err NotFound` or `Err InvalidNumStr`).
-parse_with_try = \line ->
+parse_with_try : Str -> Result Person [InvalidNumStr, NotFound]
+parse_with_try = |line|
line
- |> Str.splitFirst " was born in "
- |> Result.try \{ before: full_name, after: birth_year_str } ->
- full_name
- |> Str.splitFirst " "
- |> Result.try \{ before: first_name, after: last_name } ->
- Str.toU16 birth_year_str
- |> Result.try \birth_year ->
- Ok { first_name, last_name, birth_year }
-
-## This version is like `parseWithTry`, except it uses `Result.mapErr`
-## to return more informative errors, just like the ones in `parseVerbose`.
-parse_with_try_v2 = \line ->
+ |> Str.split_first(" was born in ")
+ |> Result.try(
+ |{ before: full_name, after: birth_year_str }|
+ full_name
+ |> Str.split_first(" ")
+ |> Result.try(
+ |{ before: first_name, after: last_name }|
+ Str.to_u16(birth_year_str)
+ |> Result.try(
+ |birth_year|
+ Ok({ first_name, last_name, birth_year }),
+ ),
+ ),
+ )
+
+## This version is like `parse_with_try`, except it uses `Result.map_err`
+## to return more informative errors, just like the ones in `parse_verbose`.
+parse_with_try_v2 : Str -> Result Person [InvalidRecordFormat, InvalidNameFormat, InvalidBirthYearFormat]
+parse_with_try_v2 = |line|
line
- |> Str.splitFirst " was born in "
- |> Result.mapErr \_ -> Err InvalidRecordFormat
- |> Result.try \{ before: full_name, after: birth_year_str } ->
- full_name
- |> Str.splitFirst " "
- |> Result.mapErr \_ -> Err InvalidNameFormat
- |> Result.try \{ before: first_name, after: last_name } ->
- Str.toU16 birth_year_str
- |> Result.mapErr \_ -> Err InvalidBirthYearFormat
- |> Result.try \birth_year ->
- Ok { first_name, last_name, birth_year }
+ |> Str.split_first(" was born in ")
+ |> Result.map_err(|_| InvalidRecordFormat)
+ |> Result.try(
+ |{ before: full_name, after: birth_year_str }|
+ full_name
+ |> Str.split_first(" ")
+ |> Result.map_err(|_| InvalidNameFormat)
+ |> Result.try(
+ |{ before: first_name, after: last_name }|
+ Str.to_u16(birth_year_str)
+ |> Result.map_err(|_| InvalidBirthYearFormat)
+ |> Result.try(
+ |birth_year|
+ Ok({ first_name, last_name, birth_year }),
+ ),
+ ),
+ )
## The `?` operator, called the "try operator", is
## [syntactic sugar](en.wikipedia.org/wiki/Syntactic_sugar) for `Result.try`.
## It makes the code much less nested and easier to read.
-## The following function is equivalent to `parseWithTry`:
-parse_with_try_op = \line ->
- { before: full_name, after: birth_year_str } = Str.splitFirst? line " was born in "
- { before: first_name, after: last_name } = Str.splitFirst? full_name " "
- birth_year = Str.toU16? birth_year_str
- Ok { first_name, last_name, birth_year }
-
-## And lastly the following function is equivalent to `parseWithTryV2`.
-## Note that the `?` operator has moved from `splitFirst` & `toU16` to `mapErr`:
-parse_with_try_op_v2 = \line ->
+## The following function is equivalent to `parse_with_try`:
+parse_with_try_op : Str -> Result Person [NotFound, InvalidNumStr]
+parse_with_try_op = |line|
+ { before: full_name, after: birth_year_str } = Str.split_first(line, " was born in ")?
+ { before: first_name, after: last_name } = Str.split_first(full_name, " ")?
+ birth_year = Str.to_u16(birth_year_str)?
+
+ Ok({ first_name, last_name, birth_year })
+
+## And lastly the following function is equivalent to `parse_with_try_v2`.
+## Note that the `?` operator has moved from `split_first` & `to_u16` to `map_err`:
+parse_with_try_op_v2 : Str -> Result Person [InvalidRecordFormat, InvalidNameFormat, InvalidBirthYearFormat]
+parse_with_try_op_v2 = |line|
{ before: full_name, after: birth_year_str } =
- line
- |> Str.splitFirst " was born in "
- |> Result.mapErr? \_ -> Err InvalidRecordFormat
+ (Str.split_first(line, " was born in ") |> Result.map_err(|_| InvalidRecordFormat))?
+
{ before: first_name, after: last_name } =
- full_name
- |> Str.splitFirst " "
- |> Result.mapErr? \_ -> Err InvalidNameFormat
- birth_year =
- Str.toU16 birth_year_str
- |> Result.mapErr? \_ -> Err InvalidBirthYearFormat
- Ok { first_name, last_name, birth_year }
+ (Str.split_first(full_name, " ") |> Result.map_err(|_| InvalidNameFormat))?
+
+ birth_year = Result.map_err(Str.to_u16(birth_year_str), |_| InvalidBirthYearFormat)?
+
+ Ok({ first_name, last_name, birth_year })
## This function parses a string using a given parser and returns a string to
## display to the user. Note how we can handle errors individually or in bulk.
-parse = \line, parser ->
- when parser line is
- Ok { first_name, last_name, birth_year } ->
+parse = |line, parser|
+ when parser(line) is
+ Ok({ first_name, last_name, birth_year }) ->
"""
- Name: $(last_name), $(first_name)
- Born: $(birth_year |> Num.toStr)
+ Name: ${last_name}, ${first_name}
+ Born: ${Num.to_str(birth_year)}
"""
- Err InvalidNameFormat -> "What kind of a name is this?"
- Err InvalidBirthYearFormat -> "That birth year looks fishy."
- Err InvalidRecordFormat -> "Oh wow, that's a weird looking record!"
+ Err(InvalidNameFormat) -> "What kind of a name is this?"
+ Err(InvalidBirthYearFormat) -> "That birth year looks fishy."
+ Err(InvalidRecordFormat) -> "Oh wow, that's a weird looking record!"
_ -> "Something unexpected happened" # Err NotFound or Err InvalidNumStr
-main! = \_args ->
- try Stdout.line! (parse "George Harrison was born in 1943" parse_verbose)
- try Stdout.line! (parse "John Lennon was born in 1940" parse_with_try)
- try Stdout.line! (parse "Paul McCartney was born in 1942" parse_with_try_v2)
- try Stdout.line! (parse "Ringo Starr was born in 1940" parse_with_try_op)
- try Stdout.line! (parse "Stuart Sutcliffe was born in 1940" parse_with_try_op_v2)
+main! = |_args|
+ Stdout.line!(parse("George Harrison was born in 1943", parse_verbose))?
+ Stdout.line!(parse("John Lennon was born in 1940", parse_with_try))?
+ Stdout.line!(parse("Paul McCartney was born in 1942", parse_with_try_v2))?
+ Stdout.line!(parse("Ringo Starr was born in 1940", parse_with_try_op))?
+ Stdout.line!(parse("Stuart Sutcliffe was born in 1940", parse_with_try_op_v2))?
+
+ Ok({})
- Ok {}
+expect parse("George Harrison was born in 1943", parse_verbose) == "Name: Harrison, George\nBorn: 1943\n"
+expect parse("John Lennon was born in 1940", parse_with_try) == "Name: Lennon, John\nBorn: 1940\n"
+expect parse("Paul McCartney was born in 1942", parse_with_try_v2) == "Name: McCartney, Paul\nBorn: 1942\n"
+expect parse("Ringo Starr was born in 1940", parse_with_try_op) == "Name: Starr, Ringo\nBorn: 1940\n"
+expect parse("Stuart Sutcliffe was born in 1940", parse_with_try_op_v2) == "Name: Sutcliffe, Stuart\nBorn: 1940\n"
diff --git a/examples/SafeMath/README.md b/examples/SafeMath/README.md
index e26ba4c..ec3412b 100644
--- a/examples/SafeMath/README.md
+++ b/examples/SafeMath/README.md
@@ -3,21 +3,21 @@
This example shows how to perform calculations while avoiding overflows.
For example; `+` actually uses `Num.add`, which can crash if the bytes of the result can not fit in the provided type:
```cli
-» Num.maxU64 + Num.maxU64
+» Num.max_u64 + Num.max_u64
This Roc code crashed with: "Integer addition overflowed!"
* : U64
```
If you want to avoid a program-ending crash, you can instead use:
```
-» Num.addChecked Num.maxU64 Num.maxU64
+» Num.add_checked Num.max_u64 Num.max_u64
Err Overflow : Result U64 [Overflow]
```
That would allow you to display a clean error to the user or handle the failure in an intelligent way.
-Use `Checked` math functions if [reliability is important for your application](https://arstechnica.com/information-technology/2015/05/boeing-787-dreamliners-contain-a-potentially-catastrophic-software-bug/).
+Use `checked` math functions if [reliability is important for your application](https://arstechnica.com/information-technology/2015/05/boeing-787-dreamliners-contain-a-potentially-catastrophic-software-bug/).
-For a realistic demonstration, we will use `Checked` math functions to calculate the variance of a population.
+For a realistic demonstration, we will use `checked` math functions to calculate the variance of a population.
The variance formula is: `σ² = ∑(X - µ)² / N` where:
- `σ²` = variance
diff --git a/examples/SafeMath/main.roc b/examples/SafeMath/main.roc
index e44de01..9880d55 100644
--- a/examples/SafeMath/main.roc
+++ b/examples/SafeMath/main.roc
@@ -1,4 +1,4 @@
-app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
import cli.Stdout
@@ -14,49 +14,50 @@ import cli.Stdout
## Performance note: safe or checked math prevents crashes but also runs slower.
##
safe_variance : List (Frac a) -> Result (Frac a) [EmptyInputList, Overflow]
-safe_variance = \maybe_empty_list ->
+safe_variance = |maybe_empty_list|
- # Check length to prevent DivByZero
- when List.len maybe_empty_list is
- 0 -> Err EmptyInputList
+ # Check length to prevent division by zero
+ when List.len(maybe_empty_list) is
+ 0 -> Err(EmptyInputList)
_ ->
non_empty_list = maybe_empty_list
- n = non_empty_list |> List.len |> Num.toFrac
+ n = non_empty_list |> List.len |> Num.to_frac
mean =
non_empty_list # sum of all elements:
- |> List.walkTry 0.0 (\state, elem -> Num.addChecked state elem)
- |> Result.map (\x -> x / n)
+ |> List.walk_try(0.0, |state, elem| Num.add_checked(state, elem))
+ |> Result.map_ok(|x| x / n)
non_empty_list
- |> List.walkTry
- 0.0
- (\state, elem ->
+ |> List.walk_try(
+ 0.0,
+ |state, elem|
mean
- |> Result.try (\m -> Num.subChecked elem m) # X - µ
- |> Result.try (\y -> Num.mulChecked y y) # ²
- |> Result.try (\z -> Num.addChecked z state)) # ∑
- |> Result.map (\x -> x / n)
+ |> Result.try(|m| Num.sub_checked(elem, m)) # X - µ
+ |> Result.try(|y| Num.mul_checked(y, y)) # ²
+ |> Result.try(|z| Num.add_checked(z, state)), # ∑
+ )
+ |> Result.map_ok(|x| x / n)
-main! = \_args ->
+main! = |_args|
variance_result =
[46, 69, 32, 60, 52, 41]
|> safe_variance
- |> Result.map Num.toStr
- |> Result.map (\v -> "σ² = $(v)")
+ |> Result.map_ok(Num.to_str)
+ |> Result.map_ok(|v| "σ² = ${v}")
output_str =
when variance_result is
- Ok str -> str
- Err EmptyInputList -> "Error: EmptyInputList: I can't calculate the variance over an empty list."
- Err Overflow -> "Error: Overflow: When calculating the variance, a number got too large to store in the available memory for the type."
+ Ok(str) -> str
+ Err(EmptyInputList) -> "Error: EmptyInputList: I can't calculate the variance over an empty list."
+ Err(Overflow) -> "Error: Overflow: When calculating the variance, a number got too large to store in the available memory for the type."
- Stdout.line! output_str
+ Stdout.line!(output_str)
-expect (safe_variance []) == Err EmptyInputList
-expect (safe_variance [0]) == Ok 0
-expect (safe_variance [100]) == Ok 0
-expect (safe_variance [4, 22, 99, 204, 18, 20]) == Ok 5032.138888888888888888
-expect (safe_variance [46, 69, 32, 60, 52, 41]) == Ok 147.666666666666666666
+expect safe_variance([]) == Err(EmptyInputList)
+expect safe_variance([0]) == Ok(0)
+expect safe_variance([100]) == Ok(0)
+expect safe_variance([4, 22, 99, 204, 18, 20]) == Ok(5032.138888888888888888)
+expect safe_variance([46, 69, 32, 60, 52, 41]) == Ok(147.666666666666666666)
diff --git a/examples/TowersOfHanoi/Hanoi.roc b/examples/TowersOfHanoi/Hanoi.roc
index 1765c30..34a60d9 100644
--- a/examples/TowersOfHanoi/Hanoi.roc
+++ b/examples/TowersOfHanoi/Hanoi.roc
@@ -7,39 +7,43 @@ State : {
from : Str, # identifier of the source rod
to : Str, # identifier of the target rod
using : Str, # identifier of the auxiliary rod
- moves : List (Str, Str), # list of moves accumulated so far
+ moves : List (Str, Str), # list of moves done so far
}
## Solves the Tower of Hanoi problem using recursion. Returns a list of moves
## which represent the solution.
hanoi : State -> List (Str, Str)
-hanoi = \{ num_disks, from, to, using, moves } ->
+hanoi = |{ num_disks, from, to, using, moves }|
if num_disks == 1 then
- List.concat moves [(from, to)]
+ List.concat(moves, [(from, to)])
else
- moves1 = hanoi {
- num_disks: (num_disks - 1),
- from,
- to: using,
- using: to,
- moves,
- }
+ moves1 = hanoi(
+ {
+ num_disks: (num_disks - 1),
+ from,
+ to: using,
+ using: to,
+ moves,
+ },
+ )
- moves2 = List.concat moves1 [(from, to)]
+ moves2 = List.concat(moves1, [(from, to)])
- hanoi {
- num_disks: (num_disks - 1),
- from: using,
- to,
- using: from,
- moves: moves2,
- }
+ hanoi(
+ {
+ num_disks: num_disks - 1,
+ from: using,
+ to,
+ using: from,
+ moves: moves2,
+ },
+ )
start = { num_disks: 0, from: "A", to: "B", using: "C", moves: [] }
## Test Case 1: Tower of Hanoi with 1 disk
expect
- actual = hanoi { start & num_disks: 1 }
+ actual = hanoi({ start & num_disks: 1 })
actual
== [
("A", "B"),
@@ -47,7 +51,7 @@ expect
## Test Case 2: Tower of Hanoi with 2 disks
expect
- actual = hanoi { start & num_disks: 2 }
+ actual = hanoi({ start & num_disks: 2 })
actual
== [
("A", "C"),
@@ -57,7 +61,7 @@ expect
## Test Case 3: Tower of Hanoi with 3 disks
expect
- actual = hanoi { start & num_disks: 3 }
+ actual = hanoi({ start & num_disks: 3 })
actual
== [
("A", "B"),
diff --git a/examples/Tuples/main.roc b/examples/Tuples/main.roc
index 5441bd7..9624569 100644
--- a/examples/Tuples/main.roc
+++ b/examples/Tuples/main.roc
@@ -1,25 +1,25 @@
-app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br" }
import pf.Stdout
-main! = \_args ->
+main! = |_args|
- # a tuple that contains different types
+ # a tuple that contains three different types
simple_tuple : (Str, Bool, I64)
simple_tuple = ("A String", Bool.true, 15_000_000)
# access the items in a tuple by index (starts at 0)
first_item = simple_tuple.0
second_item = if simple_tuple.1 then "true" else "false"
- third_item = Num.toStr simple_tuple.2
+ third_item = Num.to_str(simple_tuple.2)
- try
- Stdout.line!
- """
- First is: $(first_item),
- Second is: $(second_item),
- Third is: $(third_item).
+ Stdout.line!(
"""
+ First is: ${first_item},
+ Second is: ${second_item},
+ Third is: ${third_item}.
+ """,
+ )?
# You can also use tuples with `when`:
fruit_selection : [Apple, Pear, Banana]
@@ -29,7 +29,7 @@ main! = \_args ->
when (fruit_selection, quantity) is
# TODO re-enable when github.com/roc-lang/roc/issues/5530 is fixed.
- # (_, qty) if qty == 0 -> Stdout.line! "You also have no fruit."
- (Apple, _) -> Stdout.line! "You also have some apples."
- (Pear, _) -> Stdout.line! "You also have some pears."
- (Banana, _) -> Stdout.line! "You also have some bananas."
+ # (_, qty) if qty == 0 -> Stdout.line! "You have no fruit."
+ (Apple, _) -> Stdout.line!("You also have some apples.")
+ (Pear, _) -> Stdout.line!("You also have some pears.")
+ (Banana, _) -> Stdout.line!("You also have some bananas.")
diff --git a/flake.lock b/flake.lock
index ef4af2b..a898ba5 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,11 +3,11 @@
"flake-compat": {
"flake": false,
"locked": {
- "lastModified": 1732722421,
- "narHash": "sha256-HRJ/18p+WoXpWJkcdsk9St5ZiukCqSDgbOGFa8Okehg=",
+ "lastModified": 1733328505,
+ "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"owner": "edolstra",
"repo": "flake-compat",
- "rev": "9ed2ac151eada2306ca8c418ebd97807bb08f6ac",
+ "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"type": "github"
},
"original": {
@@ -102,11 +102,11 @@
"rust-overlay": "rust-overlay"
},
"locked": {
- "lastModified": 1735910140,
- "narHash": "sha256-v1/8HGe29QO0M9SfZruAMvHgWgm3SJPg7GFEVrXzEPM=",
+ "lastModified": 1738058275,
+ "narHash": "sha256-k3iYD2UNGPkbGePM45mWMGwp1N4uLuPWw0P/tDThLO4=",
"owner": "roc-lang",
"repo": "roc",
- "rev": "2263d8821a213629ee62193cdf56af9a2796c1c1",
+ "rev": "0e35e33f85342e2c2dda3f8e6e5414719293d759",
"type": "github"
},
"original": {
@@ -133,11 +133,11 @@
]
},
"locked": {
- "lastModified": 1732802692,
- "narHash": "sha256-kFrxb45qj52TT/OFUFyTdmvXkn/KXDUL0/DOtjHEQvs=",
+ "lastModified": 1736303309,
+ "narHash": "sha256-IKrk7RL+Q/2NC6+Ql6dwwCNZI6T6JH2grTdJaVWHF0A=",
"owner": "oxalica",
"repo": "rust-overlay",
- "rev": "34971069ec33755b2adf2481851f66d8ec9a6bfa",
+ "rev": "a0b81d4fa349d9af1765b0f0b4a899c13776f706",
"type": "github"
},
"original": {