diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index cbd04d7..663671b 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -2,27 +2,38 @@ on:
pull_request:
workflow_dispatch:
-name: Test examples on Ubuntu
+name: Test examples without nix
jobs:
test-examples:
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
- operating-system: [ubuntu-20.04, ubuntu-24.04]
+ operating-system: [ubuntu-20.04, ubuntu-24.04, macos-13, macos-14]
timeout-minutes: 90
steps:
- uses: actions/checkout@v3
+ - name: Set OS-specific variables
+ id: vars
+ run: |
+ if [[ "${{ matrix.operating-system }}" =~ ^ubuntu- ]]; then
+ echo "os_pattern=linux_x86_64" >> $GITHUB_OUTPUT
+ elif [ "${{ matrix.operating-system }}" = "macos-13" ]; then
+ echo "os_pattern=macos_x86_64" >> $GITHUB_OUTPUT
+ else
+ echo "os_pattern=macos_apple_silicon" >> $GITHUB_OUTPUT
+ fi
+
- id: try_fetching_testing_release
continue-on-error: true
run: |
- curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-TESTING.tar.gz
-
+ curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-${{ steps.vars.outputs.os_pattern }}-TESTING.tar.gz
+
- name: There are no TESTING releases, checking regular releases instead
if: steps.try_fetching_testing_release.outcome == 'failure'
run: |
- curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz
+ curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-${{ steps.vars.outputs.os_pattern }}-latest.tar.gz
- name: rename nightly tar
run: mv $(ls | grep "roc_nightly.*tar\.gz") roc_nightly.tar.gz
@@ -38,16 +49,28 @@ jobs:
- name: get short commit SHA
run: echo "SHORT_COMMIT_SHA=$(./roc_nightly/roc version | grep -oP 'commit \K[a-f0-9]+' )" >> $GITHUB_ENV
- - name: install expect # used to check if output is correct
+ - name: Install dependencies (Ubuntu)
+ if: startsWith(matrix.operating-system, 'ubuntu-')
run: sudo apt install -y expect
- - name: print runner CPU
+ - name: Install dependencies (macOS)
+ if: startsWith(matrix.operating-system, 'macos-')
+ run: |
+ brew install expect
+ brew install z3
+
+ - name: print runner CPU (Ubuntu)
+ if: startsWith(matrix.operating-system, 'ubuntu-')
run: lscpu
+ - name: print runner CPU (macOS)
+ if: startsWith(matrix.operating-system, 'macos-')
+ run: sysctl -n machdep.cpu.brand_string
+
- name: check if roc files are properly formatted
run: ROC=./roc_nightly/roc ./ci_scripts/check_format.sh
- run: ROC=./roc_nightly/roc ./ci_scripts/all_tests.sh
- name: Checks if every folder in examples is mentioned in ./examples/index.md
- run: bash ./ci_scripts/check_index.sh
+ run: bash ./ci_scripts/check_index.sh
\ No newline at end of file
diff --git a/ci_scripts/all_tests.sh b/ci_scripts/all_tests.sh
index 071a931..c2a1cbb 100755
--- a/ci_scripts/all_tests.sh
+++ b/ci_scripts/all_tests.sh
@@ -51,9 +51,6 @@ expect ci_scripts/expect_scripts/CommandLineArgs.exp
$ROC build ./examples/CommandLineArgsFile/main.roc
expect ci_scripts/expect_scripts/CommandLineArgsFile.exp
-$ROC build ./examples/DesugaringAwait/main.roc
-expect ci_scripts/expect_scripts/DesugaringAwait.exp
-
$ROC build ./examples/DesugaringTry/main.roc
$ROC test ./examples/DesugaringTry/main.roc
expect ci_scripts/expect_scripts/DesugaringTry.exp
@@ -66,11 +63,11 @@ $ROC test ./examples/TowersOfHanoi/Hanoi.roc
$ROC build ./examples/Results/main.roc
expect ci_scripts/expect_scripts/Results.exp
-$ROC build ./examples/Tasks/main.roc
-expect ci_scripts/expect_scripts/Tasks.exp
+$ROC build ./examples/ErrorHandling/main.roc
+expect ci_scripts/expect_scripts/ErrorHandling.exp
-$ROC build ./examples/TaskLoop/main.roc
-expect ci_scripts/expect_scripts/TaskLoop.exp
+$ROC build ./examples/LoopEffect/main.roc
+expect ci_scripts/expect_scripts/LoopEffect.exp
$ROC test ./examples/RecordBuilder/DateParser.roc
@@ -95,26 +92,30 @@ expect ci_scripts/expect_scripts/HelloWeb.exp
$ROC build ./examples/ImportPackageFromModule/main.roc
expect ci_scripts/expect_scripts/ImportPackageFromModule.exp
-$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
-
-# temporarily allow failure of lsb_release in case it is not installed
-set +e
-os_info=$(lsb_release -a 2>/dev/null)
-set -e
-
-# Skip Go tests if os is Ubuntu and we're not inside nix. This avoids a segfault on CI. See https://github.com/roc-lang/examples/issues/164
-if echo "$os_info" | grep -q "Ubuntu" && [ -z "${IN_NIX_SHELL}" ]; then
- echo "Skipping Go test due to https://github.com/roc-lang/examples/issues/164"
-else
- echo "Running Go test..."
- expect ci_scripts/expect_scripts/GoPlatform.exp
-fi
-
$ROC test ./examples/CustomInspect/OpaqueTypes.roc
-$ROC build ./examples/DotNetPlatform/main.roc --lib --output ./examples/DotNetPlatform/platform/interop
-expect ci_scripts/expect_scripts/DotNetPlatform.exp
+# Check if we're not on macOS, these examples don't work on macos yet
+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
+
+ # temporarily allow failure of lsb_release in case it is not installed
+ set +e
+ os_info=$(lsb_release -a 2>/dev/null)
+ set -e
+
+ # Skip Go tests if os is Ubuntu and we're not inside nix. This avoids a segfault on CI. See https://github.com/roc-lang/examples/issues/164
+ if echo "$os_info" | grep -q "Ubuntu" && [ -z "${IN_NIX_SHELL}" ]; then
+ echo "Skipping Go test due to https://github.com/roc-lang/examples/issues/164"
+ else
+ echo "Running Go test..."
+ expect ci_scripts/expect_scripts/GoPlatform.exp
+ fi
+
+
+ $ROC build ./examples/DotNetPlatform/main.roc --lib --output ./examples/DotNetPlatform/platform/interop
+ expect ci_scripts/expect_scripts/DotNetPlatform.exp
+fi
diff --git a/ci_scripts/expect_scripts/DesugaringAwait.exp b/ci_scripts/expect_scripts/DesugaringAwait.exp
deleted file mode 100644
index 6091a1d..0000000
--- a/ci_scripts/expect_scripts/DesugaringAwait.exp
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/expect
-
-# uncomment line below for debugging
-# exp_internal 1
-
-set timeout 7
-
-spawn ./examples/DesugaringAwait/main
-
-expect "Hello Alice\r\n"
-expect "Hello Bob\r\n"
-expect "Hello Alice\r\n"
-expect "Hello Bob\r\n"
-
-expect "Type in something and press Enter:\r\n"
-send "hello\r\n"
-expect "Your input was: hello\r\n"
-expect "Type in something and press Enter:\r\n"
-send "yo\r\n"
-expect "Your input was: \r\nyo\r\n" {
- expect eof
- exit 0
-}
-
-puts stderr "\nError: output was different from expected value."
-exit 1
diff --git a/ci_scripts/expect_scripts/DesugaringTry.exp b/ci_scripts/expect_scripts/DesugaringTry.exp
index 615d0c0..6314e54 100644
--- a/ci_scripts/expect_scripts/DesugaringTry.exp
+++ b/ci_scripts/expect_scripts/DesugaringTry.exp
@@ -7,7 +7,7 @@ set timeout 7
spawn ./examples/DesugaringTry/main
-expect -exact "(Ok {birthYear: 1990, name: \"Alice\"})\r\n(Ok {birthYear: 1990, name: \"Alice\"})\r\n" {
+expect -exact "(Ok {birth_year: 1990, name: \"Alice\"})\r\n(Ok {birth_year: 1990, name: \"Alice\"})\r\n" {
expect eof
exit 0
}
diff --git a/ci_scripts/expect_scripts/Tasks.exp b/ci_scripts/expect_scripts/ErrorHandling.exp
similarity index 74%
rename from ci_scripts/expect_scripts/Tasks.exp
rename to ci_scripts/expect_scripts/ErrorHandling.exp
index 0779aab..9301219 100644
--- a/ci_scripts/expect_scripts/Tasks.exp
+++ b/ci_scripts/expect_scripts/ErrorHandling.exp
@@ -5,7 +5,7 @@
set timeout 7
-spawn ./examples/Tasks/main "https://www.roc-lang.org" roc.html
+spawn ./examples/ErrorHandling/main "https://www.roc-lang.org" roc.html
expect "Done\r\n" {
expect eof
diff --git a/ci_scripts/expect_scripts/TaskLoop.exp b/ci_scripts/expect_scripts/LoopEffect.exp
similarity index 91%
rename from ci_scripts/expect_scripts/TaskLoop.exp
rename to ci_scripts/expect_scripts/LoopEffect.exp
index bd03cb2..30f881c 100644
--- a/ci_scripts/expect_scripts/TaskLoop.exp
+++ b/ci_scripts/expect_scripts/LoopEffect.exp
@@ -5,7 +5,7 @@
set timeout 7
-spawn ./examples/TaskLoop/main
+spawn ./examples/LoopEffect/main
expect "Enter some numbers on different lines, then press Ctrl-D to sum them up.\r\n"
@@ -18,7 +18,7 @@ send "\004"
expect "Sum: 2\r\n" {
expect eof {
- spawn ./examples/TaskLoop/main
+ spawn ./examples/LoopEffect/main
expect "Enter some numbers on different lines, then press Ctrl-D to sum them up.\r\n"
diff --git a/ci_scripts/expect_scripts/RandomNumbers.exp b/ci_scripts/expect_scripts/RandomNumbers.exp
index f4292b5..57041d6 100644
--- a/ci_scripts/expect_scripts/RandomNumbers.exp
+++ b/ci_scripts/expect_scripts/RandomNumbers.exp
@@ -7,11 +7,10 @@ set timeout 7
spawn ./examples/RandomNumbers/main
-
-expect "Random numbers are: 29,30,71,64,48,33,55,68,53,28\r\n" {
+expect "52\r\n34\r\n26\r\n69\r\n34\r\n35\r\n51\r\n74\r\n70\r\n39\r\n" {
expect eof
exit 0
}
puts stderr "\nError: output was different from expected value."
-exit 1
\ No newline at end of file
+exit 1
diff --git a/ci_scripts/expect_scripts/Tuples.exp b/ci_scripts/expect_scripts/Tuples.exp
index 46d1865..ab47659 100644
--- a/ci_scripts/expect_scripts/Tuples.exp
+++ b/ci_scripts/expect_scripts/Tuples.exp
@@ -7,10 +7,10 @@ set timeout 7
spawn ./examples/Tuples/main
-expect "First is: A String,\r\nSecond is: true, \r\nThird is: 15000000.\r\nYou also have some pears.\r\n" {
+expect "First is: A String,\r\nSecond is: true,\r\nThird is: 15000000.\r\nYou also have some pears.\r\n" {
expect eof
exit 0
}
puts stderr "\nError: output was different from expected value."
-exit 1
\ No newline at end of file
+exit 1
diff --git a/examples/Arithmetic/main.roc b/examples/Arithmetic/main.roc
index 430c1ea..1eb5679 100644
--- a/examples/Arithmetic/main.roc
+++ b/examples/Arithmetic/main.roc
@@ -1,37 +1,30 @@
-app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
-import pf.Arg
+import pf.Arg exposing [Arg]
-TaskErrors : [InvalidArg, InvalidNumStr]
+main! : List Arg.Arg => Result {} _
+main! = \raw_args ->
-main =
- task =
- args = readArgs!
+ args : { a : I32, b : I32 }
+ args = try read_args raw_args
- formatResult = \(operation, result) ->
- resultStr = Num.toStr result
+ result =
+ [
+ ("sum", args.a + args.b),
+ ("difference", args.a - args.b),
+ ("product", args.a * args.b),
+ ("integer quotient", args.a // args.b),
+ ("remainder", args.a % args.b),
+ ("exponentiation", Num.powInt args.a args.b),
+ ]
+ |> List.map \(operation, answer) ->
+ answer_str = Num.toStr answer
- "$(operation): $(resultStr)"
+ "$(operation): $(answer_str)"
+ |> Str.joinWith "\n"
- results =
- [
- ("sum", args.a + args.b),
- ("difference", args.a - args.b),
- ("product", args.a * args.b),
- ("integer quotient", args.a // args.b),
- ("remainder", args.a % args.b),
- ("exponentiation", Num.powInt args.a args.b),
- ]
- |> List.map formatResult
- |> Str.joinWith "\n"
-
- Task.ok results
-
- when Task.result! task is
- Ok result -> Stdout.line result
- Err InvalidArg -> Task.err (Exit 1 "Error: Please provide two integers between -1000 and 1000 as arguments.")
- Err InvalidNumStr -> Task.err (Exit 1 "Error: Invalid number format. Please provide integers between -1000 and 1000.")
+ 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
@@ -41,21 +34,26 @@ main =
## 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`.
-readArgs : Task.Task { a : I32, b : I32 } TaskErrors
-readArgs =
+read_args : List Arg -> Result { a : I32, b : I32 } [Exit I32 Str]
+read_args = \raw_args ->
+
+ 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."
args =
- Arg.list {}
- |> Task.mapErr! \_ -> InvalidArg
+ if List.len raw_args != 3 then
+ return Err invalid_args
+ else
+ List.map raw_args Arg.display
- aResult = List.get args 1 |> Result.try Str.toI32
- bResult = List.get args 2 |> Result.try Str.toI32
+ a_result = List.get args 1 |> Result.try Str.toI32
+ b_result = List.get args 2 |> Result.try Str.toI32
- when (aResult, bResult) is
+ when (a_result, b_result) is
(Ok a, Ok b) ->
if a < -1000 || a > 1000 || b < -1000 || b > 1000 then
- Task.err InvalidNumStr
+ Err invalid_num_str
else
- Task.ok { a, b }
+ Ok { a, b }
- _ -> Task.err InvalidNumStr
+ _ -> Err invalid_num_str
diff --git a/examples/BasicDict/BasicDict.roc b/examples/BasicDict/BasicDict.roc
index 897112a..2d510b0 100644
--- a/examples/BasicDict/BasicDict.roc
+++ b/examples/BasicDict/BasicDict.roc
@@ -4,8 +4,8 @@ module []
# Both key and value are type variables
# Below we use a Str key for the fruit name, and a U64 value for the fruit count.
-fruitDict : Dict Str U64
-fruitDict =
+fruit_dict : Dict Str U64
+fruit_dict =
Dict.empty {}
|> Dict.insert "Apple" 3
|> Dict.insert "Banana" 2
@@ -13,49 +13,49 @@ fruitDict =
expect
# get the value for a key
# Dict.get returns a Result with either `Ok value` or `Err KeyNotFound`
- Dict.get fruitDict "Apple" == (Ok 3)
+ Dict.get fruit_dict "Apple" == (Ok 3)
expect
# get the length (number of key-value pairs) of a Dict
- Dict.len fruitDict == 2
+ Dict.len fruit_dict == 2
expect
# convert Dict to a Str
- Inspect.toStr fruitDict == "{\"Apple\": 3, \"Banana\": 2}"
+ Inspect.toStr fruit_dict == "{\"Apple\": 3, \"Banana\": 2}"
expect
# get all the keys
- Dict.keys fruitDict == ["Apple", "Banana"]
+ Dict.keys fruit_dict == ["Apple", "Banana"]
expect
# get all the values
- Dict.values fruitDict == [3, 2]
+ Dict.values fruit_dict == [3, 2]
expect
# convert to a list of tuples
- Dict.toList fruitDict == [("Apple", 3), ("Banana", 2)]
+ Dict.toList fruit_dict == [("Apple", 3), ("Banana", 2)]
expect
# remove a key-value pair
- Dict.remove fruitDict "Apple"
+ Dict.remove fruit_dict "Apple"
|> Dict.remove "Banana"
|> Dict.isEmpty
expect
# update the value of a Dict
- updatedDict =
- Dict.update fruitDict "Apple" addFruit
+ updated_dict =
+ 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:
- addFruit : Result U64 [Missing] -> Result U64 [Missing]
- addFruit = \valueTag ->
- when valueTag is
+ add_fruit : Result U64 [Missing] -> Result U64 [Missing]
+ 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)
- Dict.get updatedDict "Apple" == (Ok 4)
+ Dict.get updated_dict "Apple" == (Ok 4)
# see https://www.roc-lang.org/builtins/Dict for more
diff --git a/examples/CommandLineArgs/main.roc b/examples/CommandLineArgs/main.roc
index 7354855..4f3644d 100644
--- a/examples/CommandLineArgs/main.roc
+++ b/examples/CommandLineArgs/main.roc
@@ -1,21 +1,21 @@
# 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.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
+app [main!] {
+ pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
}
import pf.Stdout
import pf.Arg
-main =
- args = Arg.list! {} # {} is necessary as a temporary workaround
+main! = \raw_args ->
+ args = List.map raw_args Arg.display
# get the second argument, the first is the executable's path
- argResult = List.get args 1 |> Result.mapErr (\_ -> ZeroArgsGiven)
+ arg_result = List.get args 1 |> Result.mapErr (\_ -> ZeroArgsGiven)
- when argResult is
+ when arg_result is
Err ZeroArgsGiven ->
- Task.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 (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 firstArgument ->
- Stdout.line "received argument: $(firstArgument)"
+ Ok first_argument ->
+ Stdout.line! "received argument: $(first_argument)"
diff --git a/examples/CommandLineArgsFile/main.roc b/examples/CommandLineArgsFile/main.roc
index 156c4fa..c3ab3ed 100644
--- a/examples/CommandLineArgsFile/main.roc
+++ b/examples/CommandLineArgsFile/main.roc
@@ -1,45 +1,44 @@
# 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.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
+app [main!] {
+ pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
}
import pf.Stdout
import pf.Path exposing [Path]
import pf.Arg
-main =
+main! = \raw_args ->
+
# read all command line arguments
- args = Arg.list! {} # {} is necessary as a temporary workaround
+ args = List.map raw_args Arg.display
# get the second argument, the first is the executable's path
- argResult = List.get args 1 |> Result.mapErr (\_ -> ZeroArgsGiven)
+ arg_result = List.get args 1 |> Result.mapErr \_ -> ZeroArgsGiven
- when argResult is
+ when arg_result is
Ok arg ->
- fileContentStr = readFileToStr! (Path.fromStr arg)
+ file_content_str = try read_file_to_str! (Path.from_str arg)
- Stdout.line! "file content: $(fileContentStr)"
+ Stdout.line! "file content: $(file_content_str)"
Err ZeroArgsGiven ->
- Task.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 (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
-readFileToStr : Path -> Task Str [ReadFileErr Str]_
-readFileToStr = \path ->
+read_file_to_str! : Path => Result Str [ReadFileErr Str]_
+read_file_to_str! = \path ->
path
- |> Path.readUtf8
- |> Task.mapErr # Make a nice error message
- (\fileReadErr ->
- pathStr = Path.display path
+ |> Path.read_utf8!
+ |> Result.mapErr \file_read_err ->
+ path_str = Path.display path
- when fileReadErr is
- FileReadErr _ readErr ->
- readErrStr = Inspect.toStr readErr
+ when file_read_err is
+ FileReadErr _ read_err ->
+ read_err_str = Inspect.toStr read_err
- ReadFileErr "Failed to read file:\n\t$(pathStr)\nWith error:\n\t$(readErrStr)"
+ 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$(pathStr)\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 3f20437..93cf30f 100644
--- a/examples/CustomInspect/OpaqueTypes.roc
+++ b/examples/CustomInspect/OpaqueTypes.roc
@@ -7,11 +7,11 @@ Color := [
Blue,
]
implements [
- Inspect { toInspector: colorInspector },
+ Inspect { toInspector: color_inspector },
]
-colorInspector : Color -> Inspector f where f implements InspectFormatter
-colorInspector = \@Color color ->
+color_inspector : Color -> Inspector f where f implements InspectFormatter
+color_inspector = \@Color color ->
when color is
Red -> Inspect.str "_RED_"
Green -> Inspect.str "_GREEN_"
@@ -24,11 +24,11 @@ expect Inspect.toStr (@Color Blue) == "\"_BLUE_\""
### start snippet secret
MySecret := Str implements [
- Inspect { toInspector: mySecretInspector },
+ Inspect { toInspector: my_secret_inspector },
]
-mySecretInspector : MySecret -> Inspector f where f implements InspectFormatter
-mySecretInspector = \@MySecret _ -> Inspect.str "******* REDACTED *******"
+my_secret_inspector : MySecret -> Inspector f where f implements InspectFormatter
+my_secret_inspector = \@MySecret _ -> Inspect.str "******* REDACTED *******"
expect Inspect.toStr (@MySecret "password1234") == "\"******* REDACTED *******\""
### end snippet secret
diff --git a/examples/DesugaringAwait/README.md b/examples/DesugaringAwait/README.md
deleted file mode 100644
index f6ca03e..0000000
--- a/examples/DesugaringAwait/README.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Desugaring !
-
-
- What's syntax sugar?
-
- Syntax within a programming language that is designed to make things easier
- to read or express. It allows developers to write code in a more concise, readable, or
- 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`).
-
-Let's see how `!` is desugared, we'll start with a simple example:
-```roc
-file:main.roc:snippet:bang
-```
-After desugaring, this becomes:
-```roc
-file:main.roc:snippet:await
-```
-[Task.await](https://www.roc-lang.org/builtins/Task#await) takes the success value from a given
-Task and uses that to generate a new Task.
-It's type is `Task a b, (a -> Task c b) -> Task c b`.
-
-The type of `Stdout.line` is `Str -> Task {} [StdoutErr Err]`.
-Because `Stdout.line` does not return anything upon success, we use `\_` in the desugared version,
-there is nothing to pass to the next Task.
-
-You'll see that the version with `!` looks a lot simpler!
-
-Note that for the last line in the first snippet `Stdout.line "Hello Bob"`, you could have also written
-`Stdout.line! "Hello Bob"`. `!` is not necessary on the last line but we allow it for consistency and
-to prevent confusion for beginners.
-
-`!` also makes it easy to work with variables, let's take a look:
-```roc
-file:main.roc:snippet:bangInput
-```
-
-This gets desugared to:
-```roc
-file:main.roc:snippet:awaitInput
-```
-
-This is similar to before but now the `input = Stdin.line!` gets converted to `Task.await Stdin.line \input ->`.
-With `!` you can write code in a mostly familiar way while also getting the benefits of Roc's
-error handling and the seperation of pure and effectful code.
-
-Note: this desugaring is very similar to that of [`?`](https://www.roc-lang.org/examples/DesugaringTry/README.html).
-
-## Full Code
-```roc
-file:main.roc
-```
diff --git a/examples/DesugaringAwait/main.roc b/examples/DesugaringAwait/main.roc
deleted file mode 100644
index 7325c3f..0000000
--- a/examples/DesugaringAwait/main.roc
+++ /dev/null
@@ -1,36 +0,0 @@
-app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
-
-import pf.Stdin
-import pf.Stdout
-
-main =
- helloBang!
- helloAwait!
- readInputBang!
- readInputAwait!
-
-### start snippet bang
-helloBang =
- Stdout.line! "Hello Alice"
- Stdout.line "Hello Bob"
-### end snippet bang
-
-helloAwait =
- ### start snippet await
- Task.await (Stdout.line "Hello Alice") \_ ->
- Stdout.line "Hello Bob"
-### end snippet await
-
-### start snippet bangInput
-readInputBang =
- Stdout.line! "Type in something and press Enter:"
- input = Stdin.line!
- Stdout.line! "Your input was: $(input)"
-### end snippet bangInput
-
-### start snippet awaitInput
-readInputAwait =
- Task.await (Stdout.line "Type in something and press Enter:") \_ ->
- Task.await Stdin.line \input ->
- Stdout.line "Your input was: $(input)"
-### end snippet awaitInput
diff --git a/examples/DesugaringTry/README.md b/examples/DesugaringTry/README.md
index 1e15cfb..f94a0e3 100644
--- a/examples/DesugaringTry/README.md
+++ b/examples/DesugaringTry/README.md
@@ -31,8 +31,6 @@ 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.
-Note: this desugaring is very similar to that of [`!`](https://www.roc-lang.org/examples/DesugaringAwait/README.html).
-
## Full Code
```roc
file:main.roc
diff --git a/examples/DesugaringTry/main.roc b/examples/DesugaringTry/main.roc
index 1341753..bec2ddd 100644
--- a/examples/DesugaringTry/main.roc
+++ b/examples/DesugaringTry/main.roc
@@ -1,31 +1,33 @@
-app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
-main =
- Stdout.line! (Inspect.toStr (parseNameAndYear "Alice was born in 1990"))
- Stdout.line! (Inspect.toStr (parseNameAndYearTry "Alice was born in 1990"))
+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"))
+
+ Ok {}
### start snippet question
-parseNameAndYear : Str -> Result { name : Str, birthYear : U16 } _
-parseNameAndYear = \str ->
- { before: name, after: birthYearStr } = Str.splitFirst? str " was born in "
- birthYear = Str.toU16? birthYearStr
- Ok { name, birthYear }
+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 }
### end snippet question
-parseNameAndYearTry = \str ->
+parse_name_and_year_try = \str ->
### start snippet try
str
|> Str.splitFirst " was born in "
- |> Result.try \{ before: name, after: birthYearStr } ->
- Str.toU16 birthYearStr
- |> Result.try \birthYear ->
- Ok { name, birthYear }
+ |> 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
- parseNameAndYear "Alice was born in 1990" == Ok { name: "Alice", birthYear: 1990 }
+ parse_name_and_year "Alice was born in 1990" == Ok { name: "Alice", birth_year: 1990 }
expect
- parseNameAndYearTry "Alice was born in 1990" == Ok { name: "Alice", birthYear: 1990 }
+ parse_name_and_year_try "Alice was born in 1990" == Ok { name: "Alice", birth_year: 1990 }
diff --git a/examples/EncodeDecode/main.roc b/examples/EncodeDecode/main.roc
index 74eb4d5..d436efc 100644
--- a/examples/EncodeDecode/main.roc
+++ b/examples/EncodeDecode/main.roc
@@ -1,5 +1,5 @@
-app [main] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
+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",
}
@@ -20,26 +20,26 @@ ItemKind := [
Property,
]
implements [
- Decoding { decoder: decodeItems },
- Encoding { toEncoder: encodeItems },
+ Decoding { decoder: decode_items },
+ Encoding { toEncoder: encode_items },
Inspect,
Eq,
]
-tryMapResult : DecodeResult U32, (U32 -> Result ItemKind DecodeError) -> DecodeResult ItemKind
-tryMapResult = \decoded, mapper ->
+try_map_result : DecodeResult U32, (U32 -> Result ItemKind DecodeError) -> DecodeResult ItemKind
+try_map_result = \decoded, mapper ->
when decoded.result is
Err e -> { result: Err e, rest: decoded.rest }
Ok res -> { result: mapper res, rest: decoded.rest }
-decodeItems : Decoder ItemKind fmt where fmt implements DecoderFormatting
-decodeItems = Decode.custom \bytes, fmt ->
+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
- |> tryMapResult \val ->
+ |> try_map_result \val ->
when val is
1 -> ok Text
2 -> ok Method
@@ -53,29 +53,30 @@ decodeItems = Decode.custom \bytes, fmt ->
10 -> ok Property
_ -> Err TooShort
-encodeItems : ItemKind -> Encoder fmt where fmt implements EncoderFormatting
-encodeItems = \@ItemKind val ->
- num =
- when val is
- Text -> 1
- Method -> 2
- Function -> 3
- Constructor -> 4
- Field -> 5
- Variable -> 6
- Class -> 7
- Interface -> 8
- Module -> 9
- Property -> 10
- Encode.u32 num
+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
+ )
### end snippet impl
### start snippet demo
# make a list of ItemKind's
-originalList : List ItemKind
-originalList = [
+original_list : List ItemKind
+original_list = [
@ItemKind Text,
@ItemKind Method,
@ItemKind Function,
@@ -89,28 +90,28 @@ originalList = [
]
# encode them into JSON
-encodedBytes : List U8
-encodedBytes = Encode.toBytes originalList Json.utf8
+encoded_bytes : List U8
+encoded_bytes = Encode.toBytes original_list Json.utf8
# test we have encoded correctly
-expect encodedBytes == originalBytes
+expect encoded_bytes == original_bytes
# take a JSON encoded list
-originalBytes : List U8
-originalBytes = "[1,2,3,4,5,6,7,8,9,10]" |> Str.toUtf8
+original_bytes : List U8
+original_bytes = "[1,2,3,4,5,6,7,8,9,10]" |> Str.toUtf8
# decode into a list of ItemKind's
-decodedList : List ItemKind
-decodedList = Decode.fromBytes originalBytes Json.utf8 |> Result.withDefault []
+decoded_list : List ItemKind
+decoded_list = Decode.fromBytes original_bytes Json.utf8 |> Result.withDefault []
# test we have decoded correctly
-expect decodedList == originalList
+expect decoded_list == original_list
-main =
+main! = \_args ->
# debug print decoded items to stdio
- decodedList
+ decoded_list
|> List.map Inspect.toStr
|> Str.joinWith "\n"
- |> Stdout.line
+ |> Stdout.line!
### end snippet demo
diff --git a/examples/ErrorHandling/README.md b/examples/ErrorHandling/README.md
new file mode 100644
index 0000000..f0a2aef
--- /dev/null
+++ b/examples/ErrorHandling/README.md
@@ -0,0 +1,24 @@
+# Error handling with try and Result
+
+A more complex "real world" example that demonstrates the use of `try`, `Result` and error handling in Roc.
+
+## Full Code
+
+```roc
+file:main.roc
+```
+
+## Output
+
+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 env var was set to 1
+Fetching content from https://www.roc-lang.org...
+Saving url HTML to roc.html...
+Contents of current directory: [...]
+Run time: 329 ms
+Done
+```
+
diff --git a/examples/ErrorHandling/main.roc b/examples/ErrorHandling/main.roc
new file mode 100644
index 0000000..b1a854c
--- /dev/null
+++ b/examples/ErrorHandling/main.roc
@@ -0,0 +1,91 @@
+app [main!] {
+ pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.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]
+
+usage = "HELLO=1 roc main.roc -- \"https://www.roc-lang.org\" roc.html"
+
+main! : List Arg => Result {} _
+main! = \args ->
+
+ # Get time since [Unix Epoch](https://en.wikipedia.org/wiki/Unix_time)
+ start_time : Utc
+ 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)"
+
+ try Stdout.line! "HELLO env var $(hello_env)"
+
+ # Read command line arguments
+ { url, output_path } = try parse_args! args
+
+ try Stdout.line! "Fetching content from $(url)..."
+
+ # Fetch the provided url using HTTP
+ html_str : Str
+ html_str = try fetch_html! url
+
+ try 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
+
+ # Print contents of current working directory
+ try list_cwd_contents! {}
+
+ end_time : Utc
+ end_time = Utc.now! {}
+
+ run_duration = Utc.delta_as_millis start_time end_time
+
+ try Stdout.line! "Run time: $(Num.toStr run_duration) ms"
+
+ try Stdout.line! "Done"
+
+ Ok {}
+
+parse_args! : List Arg => Result { url : Str, output_path : Path } _
+parse_args! = \args ->
+ when List.map args Arg.display is
+ [_, first, second, ..] ->
+ Ok { url: first, output_path: Path.from_str second }
+
+ _ ->
+ 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 ""
+
+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)"
+
+list_cwd_contents! : {} => Result {} _
+list_cwd_contents! = \_ ->
+
+ dirContents =
+ Dir.list! "."
+ |> Result.mapErr \_ -> FailedToListCwd "Failed to list contents of current directory, usage: $(usage)"
+ |> try
+
+ contentsStr = List.map dirContents Path.display |> Str.joinWith ","
+
+ Stdout.line! "Contents of current directory: $(contentsStr)"
diff --git a/examples/FizzBuzz/main.roc b/examples/FizzBuzz/main.roc
index da0e67f..9f85712 100644
--- a/examples/FizzBuzz/main.roc
+++ b/examples/FizzBuzz/main.roc
@@ -1,19 +1,19 @@
-app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
-main =
+main! = \_args ->
List.range { start: At 1, end: At 100 }
- |> List.map fizzBuzz
+ |> List.map fizz_buzz
|> Str.joinWith ","
- |> Stdout.line
+ |> Stdout.line!
## Determine the FizzBuzz value for a given integer.
## Returns "Fizz" for multiples of 3, "Buzz" for
## multiples of 5, "FizzBuzz" for multiples of both
## 3 and 5, and the original number for anything else.
-fizzBuzz : I32 -> Str
-fizzBuzz = \n ->
+fizz_buzz : I32 -> Str
+fizz_buzz = \n ->
fizz = n % 3 == 0
buzz = n % 5 == 0
@@ -27,17 +27,17 @@ fizzBuzz = \n ->
Num.toStr n
## Test Case 1: not a multiple of 3 or 5
-expect fizzBuzz 1 == "1"
-expect fizzBuzz 7 == "7"
+expect fizz_buzz 1 == "1"
+expect fizz_buzz 7 == "7"
## Test Case 2: multiple of 3
-expect fizzBuzz 3 == "Fizz"
-expect fizzBuzz 9 == "Fizz"
+expect fizz_buzz 3 == "Fizz"
+expect fizz_buzz 9 == "Fizz"
## Test Case 3: multiple of 5
-expect fizzBuzz 5 == "Buzz"
-expect fizzBuzz 20 == "Buzz"
+expect fizz_buzz 5 == "Buzz"
+expect fizz_buzz 20 == "Buzz"
## Test Case 4: multiple of both 3 and 5
-expect fizzBuzz 15 == "FizzBuzz"
-expect fizzBuzz 45 == "FizzBuzz"
+expect fizz_buzz 15 == "FizzBuzz"
+expect fizz_buzz 45 == "FizzBuzz"
diff --git a/examples/GraphTraversal/Graph.roc b/examples/GraphTraversal/Graph.roc
index f7aed76..21f0811 100644
--- a/examples/GraphTraversal/Graph.roc
+++ b/examples/GraphTraversal/Graph.roc
@@ -4,8 +4,8 @@
## performing a depth-first or breadth-first search.
module [
Graph,
- fromList,
- fromDict,
+ from_list,
+ from_dict,
dfs,
bfs,
]
@@ -15,19 +15,18 @@ module [
Graph a := Dict a (List a) where a implements Eq
## Create a Graph from an adjacency list.
-fromList : List (a, List a) -> Graph a
-fromList = \adjacencyList ->
- emptyDict = Dict.withCapacity (List.len adjacencyList)
+from_list : List (a, List a) -> Graph a
+from_list = \adjacency_list ->
+ empty_dict = Dict.withCapacity (List.len adjacency_list)
update = \dict, (vertex, edges) ->
Dict.insert dict vertex edges
- List.walk adjacencyList emptyDict update
- |> @Graph
+ @Graph (List.walk adjacency_list empty_dict update)
## Create a Graph from an adjacency list.
-fromDict : Dict a (List a) -> Graph a
-fromDict = @Graph
+from_dict : Dict a (List a) -> Graph a
+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)
@@ -36,8 +35,8 @@ fromDict = @Graph
## - `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 = \isTarget, root, @Graph graph ->
- dfsHelper isTarget [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.
#
@@ -45,8 +44,8 @@ dfs = \isTarget, root, @Graph graph ->
# `stack` : A List of vertices to visit.
# `visited` : A Set of visited vertices.
# `graph` : The graph to perform the search on.
-dfsHelper : (a -> Bool), List a, Set a, Dict a (List a) -> Result a [NotFound]
-dfsHelper = \isTarget, stack, visited, graph ->
+dfs_helper : (a -> Bool), List a, Set a, Dict a (List a) -> Result a [NotFound]
+dfs_helper = \is_target, stack, visited, graph ->
when stack is
[] ->
Err NotFound
@@ -54,28 +53,28 @@ dfsHelper = \isTarget, stack, visited, graph ->
[.., current] ->
rest = List.dropLast stack 1
- if isTarget current then
+ if is_target current then
Ok current
else if Set.contains visited current then
- dfsHelper isTarget rest visited graph
+ dfs_helper is_target rest visited graph
else
- newVisited = Set.insert visited current
+ new_visited = Set.insert visited current
when Dict.get graph current is
Ok neighbors ->
# filter out all visited neighbors
filtered =
neighbors
- |> List.keepIf (\n -> !(Set.contains newVisited n))
+ |> List.keepIf (\n -> !(Set.contains new_visited n))
|> List.reverse
# newly explored nodes are added to LIFO stack
- newStack = List.concat rest filtered
+ new_stack = List.concat rest filtered
- dfsHelper isTarget newStack newVisited graph
+ dfs_helper is_target new_stack new_visited graph
Err KeyNotFound ->
- dfsHelper isTarget rest newVisited graph
+ 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)
@@ -84,8 +83,8 @@ dfsHelper = \isTarget, stack, visited, graph ->
## - `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 = \isTarget, root, @Graph graph ->
- bfsHelper isTarget [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.
#
@@ -93,8 +92,8 @@ bfs = \isTarget, root, @Graph graph ->
# `queue` : A List of vertices to visit.
# `seen` : A Set of all seen vertices.
# `graph` : The graph to perform the search on.
-bfsHelper : (a -> Bool), List a, Set a, Dict a (List a) -> Result a [NotFound]
-bfsHelper = \isTarget, queue, seen, graph ->
+bfs_helper : (a -> Bool), List a, Set a, Dict a (List a) -> Result a [NotFound]
+bfs_helper = \is_target, queue, seen, graph ->
when queue is
[] ->
Err NotFound
@@ -102,7 +101,7 @@ bfsHelper = \isTarget, queue, seen, graph ->
[current, ..] ->
rest = List.dropFirst queue 1
- if isTarget current then
+ if is_target current then
Ok current
else
when Dict.get graph current is
@@ -111,74 +110,74 @@ bfsHelper = \isTarget, queue, seen, graph ->
filtered = List.keepIf neighbors (\n -> !(Set.contains seen n))
# newly explored nodes are added to the FIFO queue
- newQueue = List.concat rest filtered
+ new_queue = List.concat rest filtered
# the new nodes are also added to the seen set
- newSeen = List.walk filtered seen Set.insert
+ new_seen = List.walk filtered seen Set.insert
- bfsHelper isTarget newQueue newSeen graph
+ bfs_helper is_target new_queue new_seen graph
Err KeyNotFound ->
- bfsHelper isTarget rest seen graph
+ bfs_helper is_target rest seen graph
# Test DFS with multiple paths
expect
- actual = dfs (\v -> Str.startsWith v "C") "A" testGraphMultipath
+ actual = dfs (\v -> Str.startsWith 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" testGraphMultipath
+ actual = bfs (\v -> Str.startsWith v "C") "A" test_graph_multipath
expected = Ok "Ccorrect"
actual == expected
# Test DFS
expect
- actual = dfs (\v -> Str.startsWith v "F") "A" testGraphSmall
+ actual = dfs (\v -> Str.startsWith v "F") "A" test_graph_small
expected = Ok "F-DFS"
actual == expected
## Test BFS
expect
- actual = bfs (\v -> Str.startsWith v "F") "A" testGraphSmall
+ actual = bfs (\v -> Str.startsWith v "F") "A" test_graph_small
expected = Ok "F-BFS"
actual == expected
# Test NotFound DFS
expect
- actual = dfs (\v -> v == "not a node") "A" testGraphSmall
+ 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" testGraphSmall
+ 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" testGraphLarge
+ actual = dfs (\v -> v == "AE") "A" test_graph_large
expected = Ok "AE"
actual == expected
## Test BFS large
expect
- actual = bfs (\v -> v == "AE") "A" testGraphLarge
+ actual = bfs (\v -> v == "AE") "A" test_graph_large
expected = Ok "AE"
actual == expected
# Some helpers for testing
-testGraphSmall =
+test_graph_small =
[
("A", ["B", "C", "F-BFS"]),
("B", ["D", "E"]),
@@ -188,9 +187,9 @@ testGraphSmall =
("F-BFS", []),
("F-DFS", []),
]
- |> fromList
+ |> from_list
-testGraphLarge =
+test_graph_large =
[
("A", ["B", "C", "D"]),
("B", ["E", "F", "G"]),
@@ -224,13 +223,13 @@ testGraphLarge =
("AD", []),
("AE", []),
]
- |> fromList
+ |> from_list
-testGraphMultipath =
+test_graph_multipath =
[
("A", ["B", "Ccorrect"]),
("B", ["Ccorrect", "Cwrong"]),
("Ccorrect", []),
("Cwrong", []),
]
- |> fromList
+ |> from_list
diff --git a/examples/HelloWeb/main.roc b/examples/HelloWeb/main.roc
index a496cdc..3b3f351 100644
--- a/examples/HelloWeb/main.roc
+++ b/examples/HelloWeb/main.roc
@@ -1,4 +1,4 @@
-app [Model, server] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.10.0/BgDDIykwcg51W8HA58FE_BjdzgXVk--ucv6pVb_Adik.tar.br" }
+app [Model, init!, respond!] { pf: platform "https://github.com/roc-lang/basic-webserver/releases/download/0.11.0/yWHkcVUt_WydE1VswxKFmKFM5Tlu9uMn6ctPVYaas7I.tar.br" }
import pf.Stdout
import pf.Http exposing [Request, Response]
@@ -11,13 +11,18 @@ Model : {}
# generate css by running `tailwindcss`,...
# In this case we don't have anything to initialize, so it is just `Task.ok {}`.
-server = { init: Task.ok {}, respond }
+init! : {} => Result Model []
+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
+ datetime = Utc.to_iso_8601 (Utc.now! {})
- Stdout.line! "$(datetime) $(Http.methodToStr req.method) $(req.url)"
+ try Stdout.line! "$(datetime) $(Inspect.toStr req.method) $(req.uri)"
- Task.ok { status: 200, headers: [], body: Str.toUtf8 "Hello, web!" }
+ Ok {
+ status: 200,
+ headers: [],
+ body: Str.toUtf8 "Hello, web!",
+ }
diff --git a/examples/HelloWorld/main.roc b/examples/HelloWorld/main.roc
index 5157acb..66edde3 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.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
-main =
+main! = \_args ->
Stdout.line! "Hello, World!"
diff --git a/examples/ImportFromDirectory/Dir/Hello.roc b/examples/ImportFromDirectory/Dir/Hello.roc
index 80a62b3..fae9d72 100644
--- a/examples/ImportFromDirectory/Dir/Hello.roc
+++ b/examples/ImportFromDirectory/Dir/Hello.roc
@@ -1,6 +1,5 @@
-module
- # Only what's listed here is accessible to other modules
- [hello]
+# Only what's listed here is accessible to other modules
+module [hello]
hello : Str -> Str
hello = \name ->
diff --git a/examples/ImportFromDirectory/main.roc b/examples/ImportFromDirectory/main.roc
index 2cbf0cd..cce91ee 100644
--- a/examples/ImportFromDirectory/main.roc
+++ b/examples/ImportFromDirectory/main.roc
@@ -1,7 +1,8 @@
-app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
import Dir.Hello exposing [hello]
-main =
+main! = \_args ->
+ # here we're calling the `hello` function from the Hello module
Stdout.line! (hello "World")
diff --git a/examples/ImportPackageFromModule/Module.roc b/examples/ImportPackageFromModule/Module.roc
index 7b7c3de..bb62ffa 100644
--- a/examples/ImportPackageFromModule/Module.roc
+++ b/examples/ImportPackageFromModule/Module.roc
@@ -1,6 +1,5 @@
-module [splitGraphemes]
+module [split_graphemes]
import unicode.Grapheme
-splitGraphemes = \string -> Grapheme.split string
-
+split_graphemes = \string -> Grapheme.split string
diff --git a/examples/ImportPackageFromModule/main.roc b/examples/ImportPackageFromModule/main.roc
index 4e0432d..038fcbe 100644
--- a/examples/ImportPackageFromModule/main.roc
+++ b/examples/ImportPackageFromModule/main.roc
@@ -1,6 +1,6 @@
### start snippet header
-app [main] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
+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",
}
### end snippet header
@@ -8,5 +8,5 @@ app [main] {
import pf.Stdout
import Module
-main =
- Stdout.line! (Inspect.toStr (Module.splitGraphemes "hello"))
+main! = \_args ->
+ Stdout.line! (Inspect.toStr (Module.split_graphemes "hello"))
diff --git a/examples/IngestFiles/main.roc b/examples/IngestFiles/main.roc
index cc2d06e..f78c382 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.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
import "sample.txt" as sample : Str
-main =
+main! = \_args ->
Stdout.line! "$(sample)"
diff --git a/examples/Json/main.roc b/examples/Json/main.roc
index 98bd54f..0200ae2 100644
--- a/examples/Json/main.roc
+++ b/examples/Json/main.roc
@@ -1,14 +1,13 @@
-app [main] {
- cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
+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",
}
import cli.Stdout
import json.Json
-import Decode exposing [fromBytesPartial]
-main =
- requestBody = 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.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}}"
# This { fieldNameMapping: PascalCase } setting translates
# incoming JSON fields from PascalCase (first letter capitalized)
@@ -17,11 +16,11 @@ main =
decoder = Json.utf8With { fieldNameMapping: PascalCase }
decoded : DecodeResult ImageRequest
- decoded = fromBytesPartial requestBody decoder
+ decoded = Decode.fromBytesPartial request_body decoder
when decoded.result is
- Ok record -> Stdout.line "Successfully decoded image, title:\"$(record.image.title)\""
- Err _ -> Task.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 ace6e19..1ed6113 100644
--- a/examples/LeastSquares/main.roc
+++ b/examples/LeastSquares/main.roc
@@ -1,11 +1,11 @@
-app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
-main =
- nStr = Num.toStr (leastSquareDifference {})
+main! = \_args ->
+ nStr = Num.toStr (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 $(nStr)"
## 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)`
@@ -14,16 +14,16 @@ main =
## The input `n` should be a positive integer, and the function will return an
## `U32`representing the least positive integer that satisfies the condition.
##
-leastSquareDifference : {} -> U32
-leastSquareDifference = \_ ->
- findNumber = \n ->
+least_square_difference : {} -> U32
+least_square_difference = \_ ->
+ find_number = \n ->
difference = (Num.powInt n 2) - (Num.powInt (n - 1) 2)
if difference > 1000 then
n
else
- findNumber (n + 1)
+ find_number (n + 1)
- findNumber 1
+ find_number 1
-expect leastSquareDifference {} == 501
+expect least_square_difference {} == 501
diff --git a/examples/LoopEffect/README.md b/examples/LoopEffect/README.md
new file mode 100644
index 0000000..cf527cf
--- /dev/null
+++ b/examples/LoopEffect/README.md
@@ -0,0 +1,20 @@
+# 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.
+
+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)).
+
+## Full Code
+
+```roc
+file:main.roc
+```
+
+## Output
+
+Run this from the directory that has `main.roc` in it:
+
+```
+$ roc main.roc < numbers.txt
+Enter some numbers on different lines, then press Ctrl-D to sum them up.
+Sum: 178
+```
diff --git a/examples/LoopEffect/main.roc b/examples/LoopEffect/main.roc
new file mode 100644
index 0000000..754b43d
--- /dev/null
+++ b/examples/LoopEffect/main.roc
@@ -0,0 +1,37 @@
+app [main!] {
+ pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
+}
+
+import pf.Stdin
+import pf.Stdout
+import pf.Stderr
+
+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."
+
+ sum = try add_number_from_stdin! 0
+
+ Stdout.line! "Sum: $(Num.toStr sum)"
+
+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)
+
+ Err EndOfFile -> Ok sum
+ Err err -> err |> Inspect.toStr |> NotNum |> Err
+
+print_err! : _ => Result {} _
+print_err! = \err ->
+ when err is
+ NotNum text -> Stderr.line! "Error: \"$(text)\" is not a valid I64 number."
+ _ -> Stderr.line! "Error: $(Inspect.toStr err)"
diff --git a/examples/TaskLoop/numbers.txt b/examples/LoopEffect/numbers.txt
similarity index 100%
rename from examples/TaskLoop/numbers.txt
rename to examples/LoopEffect/numbers.txt
diff --git a/examples/MultipleRocFiles/Hello.roc b/examples/MultipleRocFiles/Hello.roc
index 38fb1fd..602ab0a 100644
--- a/examples/MultipleRocFiles/Hello.roc
+++ b/examples/MultipleRocFiles/Hello.roc
@@ -1,6 +1,5 @@
-module
- # Only what's listed here is accessible/exposed to other modules
- [hello]
+# Only what's listed here is accessible/exposed to other modules
+module [hello]
hello : Str -> Str
hello = \name ->
diff --git a/examples/MultipleRocFiles/main.roc b/examples/MultipleRocFiles/main.roc
index 3ea6a1f..f5923e2 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.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
import Hello
-main =
+main! = \_args ->
Stdout.line! (Hello.hello "World")
diff --git a/examples/Parser/main.roc b/examples/Parser/main.roc
index 6a8e747..2d4690e 100644
--- a/examples/Parser/main.roc
+++ b/examples/Parser/main.roc
@@ -1,5 +1,5 @@
-app [main] {
- cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
+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",
}
@@ -7,31 +7,34 @@ import cli.Stdout
import parser.Parser exposing [Parser, many, oneOf, map]
import parser.String exposing [parseStr, codeunit, anyCodeunit]
-main =
- many letterParser
- |> parseStr inputStr
- |> Result.map countLetterAs
- |> Result.map \count -> "I counted $(count) letter A's!"
- |> Result.withDefault "Ooops, something went wrong parsing"
- |> Stdout.line!
+main! = \_args ->
+
+ letters = try parseStr (many letter_parser) input_str
+
+ msg =
+ letters
+ |> count_letter_a
+ |> \count -> "I counted $(count) letter A's!"
+
+ Stdout.line! msg
Letter : [A, B, C, Other]
-inputStr = "AAAiBByAABBwBtCCCiAyArBBx"
+input_str = "AAAiBByAABBwBtCCCiAyArBBx"
# Helper to check if a letter is an A tag
isA = \l -> l == A
# Count the number of Letter A's
-countLetterAs : List Letter -> Str
-countLetterAs = \letters ->
+count_letter_a : List Letter -> Str
+count_letter_a = \letters ->
letters
|> List.countIf isA
|> Num.toStr
# Parser to convert utf8 input into Letter tags
-letterParser : Parser (List U8) Letter
-letterParser =
+letter_parser : Parser (List U8) Letter
+letter_parser =
oneOf [
codeunit 'A' |> map \_ -> A,
codeunit 'B' |> map \_ -> B,
@@ -42,13 +45,13 @@ letterParser =
# Test we can parse a single B letter
expect
input = "B"
- parser = letterParser
+ parser = letter_parser
result = parseStr parser input
result == Ok B
# Test we can parse a number of different letters
expect
input = "BCXA"
- parser = many letterParser
+ parser = many letter_parser
result = parseStr parser input
result == Ok [B, C, Other, A]
diff --git a/examples/RandomNumbers/README.md b/examples/RandomNumbers/README.md
index 09e87f6..e5b4ef9 100644
--- a/examples/RandomNumbers/README.md
+++ b/examples/RandomNumbers/README.md
@@ -19,5 +19,14 @@ Run this from the directory that has `main.roc` in it:
```
$ roc main.roc
-Random numbers are: 29,30,71,64,48,33,55,68,53,28
+52
+34
+26
+69
+34
+35
+51
+74
+70
+39
```
diff --git a/examples/RandomNumbers/main.roc b/examples/RandomNumbers/main.roc
index 7d1b58d..6608d7f 100644
--- a/examples/RandomNumbers/main.roc
+++ b/examples/RandomNumbers/main.roc
@@ -1,42 +1,35 @@
-app [main] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
- rand: "https://github.com/lukewilliamboswell/roc-random/releases/download/0.0.1/x_XwrgehcQI4KukXligrAkWTavqDAdE5jGamURpaX-M.tar.br",
+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",
}
import pf.Stdout
import rand.Random
-# Print a list of 10 random numbers in the range 25-75 inclusive.
-main =
+main! = \_args ->
- # Initialize "randomness"
- initialSeed = Random.seed 42
-
- # Create a generator for values from 25-75 (inclusive)
- generator = Random.int 25 75
-
- # Create a list of random numbers
- result = randomList initialSeed generator
-
- # Format as a string
- numbersListStr =
- result.numbers
+ # Print a list of 10 random numbers in the range 25-75 inclusive.
+ numbers_str =
+ randomNumbers
|> List.map Num.toStr
- |> Str.joinWith ","
- Stdout.line! "Random numbers are: $(numbersListStr)"
+ |> Str.joinWith "\n"
-# Generate a list of numbers using the seed and generator provided
+ Stdout.line! numbers_str
+
+# Generate a list random numbers using the seed `1234`.
# This is NOT cryptograhpically secure!
-randomList = \initialSeed, generator ->
- List.range { start: At 0, end: Before 10 }
- |> List.walk { seed: initialSeed, numbers: [] } \state, _ ->
- # Use the generator to get a new seed and value
- random = generator state.seed
+randomNumbers : List U32
+randomNumbers =
+ { value: numbers } = Random.step (Random.seed 1234) numbersGenerator
- # Update seed so it can be used to generate the next value
- seed = random.state
+ numbers
- # Append the latest random value to the list of numbers
- numbers = List.append state.numbers random.value
+# A generator that will produce a list of 10 random numbers in the range 25-75 inclusive.
+# This is NOT cryptograhpically secure!
+numbersGenerator : Random.Generator (List U32)
+numbersGenerator =
+ Random.list (Random.boundedU32 25 75) 10
- { seed, numbers }
+expect
+ actual = randomNumbers
+ actual == [52, 34, 26, 69, 34, 35, 51, 74, 70, 39]
diff --git a/examples/RecordBuilder/DateParser.roc b/examples/RecordBuilder/DateParser.roc
index 908f2cb..7790ce3 100644
--- a/examples/RecordBuilder/DateParser.roc
+++ b/examples/RecordBuilder/DateParser.roc
@@ -1,17 +1,17 @@
module [
ParserGroup,
ParserErr,
- parseWith,
- chainParsers,
- buildSegmentParser,
+ parse_with,
+ chain_parsers,
+ build_segment_parser,
]
ParserErr : [InvalidNumStr, OutOfSegments]
ParserGroup a := List Str -> Result (a, List Str) ParserErr
-parseWith : (Str -> Result a ParserErr) -> ParserGroup a
-parseWith = \parser ->
+parse_with : (Str -> Result a ParserErr) -> ParserGroup a
+parse_with = \parser ->
@ParserGroup \segments ->
when segments is
[] -> Err OutOfSegments
@@ -19,31 +19,31 @@ parseWith = \parser ->
parsed = parser? first
Ok (parsed, rest)
-chainParsers : ParserGroup a, ParserGroup b, (a, b -> c) -> ParserGroup c
-chainParsers = \@ParserGroup first, @ParserGroup second, combiner ->
+chain_parsers : ParserGroup a, ParserGroup b, (a, b -> c) -> ParserGroup c
+chain_parsers = \@ParserGroup first, @ParserGroup second, combiner ->
@ParserGroup \segments ->
- (a, afterFirst) = first? segments
- (b, afterSecond) = second? afterFirst
+ (a, after_first) = first? segments
+ (b, after_second) = second? after_first
- Ok (combiner a b, afterSecond)
+ Ok (combiner a b, after_second)
-buildSegmentParser : ParserGroup a -> (Str -> Result a ParserErr)
-buildSegmentParser = \@ParserGroup parserGroup ->
+build_segment_parser : ParserGroup a -> (Str -> Result a ParserErr)
+build_segment_parser = \@ParserGroup parser_group ->
\text ->
segments = Str.splitOn text "-"
- (date, _remaining) = parserGroup? segments
+ (date, _remaining) = parser_group? segments
Ok date
expect
- 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.toU64,
+ year: parse_with Str.toU64,
}
- |> buildSegmentParser
+ |> build_segment_parser
- date = dateParser "Mar-10-2015"
+ date = date_parser "Mar-10-2015"
date == Ok { month: "Mar", day: 10, year: 2015 }
diff --git a/examples/Results/README.md b/examples/Results/README.md
index aac0d6d..3b42141 100644
--- a/examples/Results/README.md
+++ b/examples/Results/README.md
@@ -1,5 +1,7 @@
# 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.
## Code
diff --git a/examples/Results/main.roc b/examples/Results/main.roc
index bae6dc6..7d1a380 100644
--- a/examples/Results/main.roc
+++ b/examples/Results/main.roc
@@ -1,5 +1,5 @@
-app [main] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
+app [main!] {
+ pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br",
}
import pf.Stdout
@@ -8,14 +8,14 @@ import pf.Stdout
## and if successful returns `Ok {firstName, lastName, birthYear}`. Otherwise
## it returns an `Err` containing a descriptive tag.
## This is the most verbose version, we will do better below.
-parseVerbose = \line ->
+parse_verbose = \line ->
when line |> Str.splitFirst " was born in " is
- Ok { before: fullName, after: birthYearStr } ->
- when fullName |> Str.splitFirst " " is
- Ok { before: firstName, after: lastName } ->
- when Str.toU16 birthYearStr is
- Ok birthYear ->
- Ok { firstName, lastName, birthYear }
+ 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 }
Err _ -> Err InvalidBirthYearFormat
@@ -26,67 +26,67 @@ parseVerbose = \line ->
## 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`).
-parseWithTry = \line ->
+parse_with_try = \line ->
line
|> Str.splitFirst " was born in "
- |> Result.try \{ before: fullName, after: birthYearStr } ->
- fullName
+ |> Result.try \{ before: full_name, after: birth_year_str } ->
+ full_name
|> Str.splitFirst " "
- |> Result.try \{ before: firstName, after: lastName } ->
- Str.toU16 birthYearStr
- |> Result.try \birthYear ->
- Ok { firstName, lastName, birthYear }
+ |> 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`.
-parseWithTryV2 = \line ->
+parse_with_try_v2 = \line ->
line
|> Str.splitFirst " was born in "
|> Result.mapErr \_ -> Err InvalidRecordFormat
- |> Result.try \{ before: fullName, after: birthYearStr } ->
- fullName
+ |> Result.try \{ before: full_name, after: birth_year_str } ->
+ full_name
|> Str.splitFirst " "
|> Result.mapErr \_ -> Err InvalidNameFormat
- |> Result.try \{ before: firstName, after: lastName } ->
- Str.toU16 birthYearStr
+ |> Result.try \{ before: first_name, after: last_name } ->
+ Str.toU16 birth_year_str
|> Result.mapErr \_ -> Err InvalidBirthYearFormat
- |> Result.try \birthYear ->
- Ok { firstName, lastName, birthYear }
+ |> 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`:
-parseWithTryOp = \line ->
- { before: fullName, after: birthYearStr } = Str.splitFirst? line " was born in "
- { before: firstName, after: lastName } = Str.splitFirst? fullName " "
- birthYear = Str.toU16? birthYearStr
- Ok { firstName, lastName, birthYear }
+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`:
-parseWithTryOpV2 = \line ->
- { before: fullName, after: birthYearStr } =
+parse_with_try_op_v2 = \line ->
+ { before: full_name, after: birth_year_str } =
line
|> Str.splitFirst " was born in "
|> Result.mapErr? \_ -> Err InvalidRecordFormat
- { before: firstName, after: lastName } =
- fullName
+ { before: first_name, after: last_name } =
+ full_name
|> Str.splitFirst " "
|> Result.mapErr? \_ -> Err InvalidNameFormat
- birthYear =
- Str.toU16 birthYearStr
+ birth_year =
+ Str.toU16 birth_year_str
|> Result.mapErr? \_ -> Err InvalidBirthYearFormat
- Ok { firstName, lastName, birthYear }
+ 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 { firstName, lastName, birthYear } ->
+ Ok { first_name, last_name, birth_year } ->
"""
- Name: $(lastName), $(firstName)
- Born: $(birthYear |> Num.toStr)
+ Name: $(last_name), $(first_name)
+ Born: $(birth_year |> Num.toStr)
"""
@@ -95,9 +95,11 @@ parse = \line, parser ->
Err InvalidRecordFormat -> "Oh wow, that's a weird looking record!"
_ -> "Something unexpected happened" # Err NotFound or Err InvalidNumStr
-main =
- "George Harrison was born in 1943" |> parse parseVerbose |> Stdout.line!
- "John Lennon was born in 1940" |> parse parseWithTry |> Stdout.line!
- "Paul McCartney was born in 1942" |> parse parseWithTryV2 |> Stdout.line!
- "Ringo Starr was born in 1940" |> parse parseWithTryOp |> Stdout.line!
- "Stuart Sutcliffe was born in 1940" |> parse parseWithTryOpV2 |> Stdout.line!
+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)
+
+ Ok {}
diff --git a/examples/SafeMath/main.roc b/examples/SafeMath/main.roc
index b560703..e44de01 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.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import cli.Stdout
@@ -13,23 +13,23 @@ import cli.Stdout
##
## Performance note: safe or checked math prevents crashes but also runs slower.
##
-safeVariance : List (Frac a) -> Result (Frac a) [EmptyInputList, Overflow]
-safeVariance = \maybeEmptyList ->
+safe_variance : List (Frac a) -> Result (Frac a) [EmptyInputList, Overflow]
+safe_variance = \maybe_empty_list ->
# Check length to prevent DivByZero
- when List.len maybeEmptyList is
+ when List.len maybe_empty_list is
0 -> Err EmptyInputList
_ ->
- nonEmptyList = maybeEmptyList
+ non_empty_list = maybe_empty_list
- n = nonEmptyList |> List.len |> Num.toFrac
+ n = non_empty_list |> List.len |> Num.toFrac
mean =
- nonEmptyList # sum of all elements:
+ non_empty_list # sum of all elements:
|> List.walkTry 0.0 (\state, elem -> Num.addChecked state elem)
|> Result.map (\x -> x / n)
- nonEmptyList
+ non_empty_list
|> List.walkTry
0.0
(\state, elem ->
@@ -39,24 +39,24 @@ safeVariance = \maybeEmptyList ->
|> Result.try (\z -> Num.addChecked z state)) # ∑
|> Result.map (\x -> x / n)
-main =
+main! = \_args ->
- varianceResult =
+ variance_result =
[46, 69, 32, 60, 52, 41]
- |> safeVariance
+ |> safe_variance
|> Result.map Num.toStr
|> Result.map (\v -> "σ² = $(v)")
- outputStr =
- when varianceResult is
+ 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."
- Stdout.line outputStr
+ Stdout.line! output_str
-expect (safeVariance []) == Err EmptyInputList
-expect (safeVariance [0]) == Ok 0
-expect (safeVariance [100]) == Ok 0
-expect (safeVariance [4, 22, 99, 204, 18, 20]) == Ok 5032.138888888888888888
-expect (safeVariance [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/TaskLoop/README.md b/examples/TaskLoop/README.md
deleted file mode 100644
index 95854b1..0000000
--- a/examples/TaskLoop/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# Looping Tasks
-
-Sometimes, you need to repeat a task, or a chain of tasks, multiple times until a particular event occurs. In roc, you can use `Task.loop` 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)).
-
-`Task.loop` starts from an initial state and sends it to a provided function that will return a new Task.
-- If this new Task matches `Task.ok (Step newState)` then the loop continues with a new call of the same function, now using `newState`.
-- If the Task is of the form `Task.ok (Done finalState)` then the loop stops and returns `finalState`.
-- If the Task is a `Task.err err`, then the loop stops and returns the error.
-
-## Code step by step
-
-```roc
-Task.loop 0 addNumberFromStdin
-```
-We provide `loop` with:
-- our initial state; the number 0
-- a function that can ingest a number and return a Task; `addNumberFromStdin`
-
-This function takes our current sum total, reads a line from stdin and returns one of the following:
-
-- `Task.ok (Step newSum)`
-- `Task.ok (Done finalSum)` on Ctrl-D or [end of file](https://en.wikipedia.org/wiki/End-of-file).
-- `Task.err (NotNum Str)` if something other than a number was provided to stdin.
-
-Take a moment to match this behavior with the type signature of `addNumberFromStdin`:
-```roc
-I64 -> Task [Done I64, Step I64] [NotNum Str]
-```
-
-This is where the action happens:
-```roc
-addNumberFromStdin = \sum ->
- input = Stdin.line!
-
- addResult =
- when input is
- Input text ->
- when Str.toI64 text is
- Ok num ->
- Ok (Step (sum + num))
-
- Err InvalidNumStr ->
- Err (NotNum text)
-
- End -> Ok (Done sum)
-
- Task.fromResult addResult
-```
-
-## Full Code
-
-```roc
-file:main.roc
-```
-
-## Output
-
-Run this from the directory that has `main.roc` in it:
-
-```
-$ roc main.roc < numbers.txt
-Enter some numbers on different lines, then press Ctrl-D to sum them up.
-Sum: 178
-```
diff --git a/examples/TaskLoop/main.roc b/examples/TaskLoop/main.roc
deleted file mode 100644
index de5f24a..0000000
--- a/examples/TaskLoop/main.roc
+++ /dev/null
@@ -1,34 +0,0 @@
-app [main] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
-}
-
-import pf.Stdin
-import pf.Stdout
-import pf.Stderr
-
-main = run |> Task.onErr printErr
-
-run : Task {} _
-run =
- Stdout.line! "Enter some numbers on different lines, then press Ctrl-D to sum them up."
-
- sum = Task.loop! 0 addNumberFromStdin
- Stdout.line! "Sum: $(Num.toStr sum)"
-
-addNumberFromStdin : I64 -> Task [Done I64, Step I64] _
-addNumberFromStdin = \sum ->
- when Stdin.line |> Task.result! is
- Ok input ->
- when Str.toI64 input is
- Ok num -> Task.ok (Step (sum + num))
- Err _ -> Task.err (NotNum input)
-
- Err (StdinErr EndOfFile) -> Task.ok (Done sum)
- Err err -> err |> Inspect.toStr |> NotNum |> Task.err
-
-printErr : _ -> Task {} _
-printErr = \err ->
- when err is
- NotNum text -> Stderr.line "Error: \"$(text)\" is not a valid I64 number."
- _ -> Stderr.line "Error: $(Inspect.toStr err)"
-
diff --git a/examples/Tasks/README.md b/examples/Tasks/README.md
deleted file mode 100644
index 2c25a42..0000000
--- a/examples/Tasks/README.md
+++ /dev/null
@@ -1,159 +0,0 @@
-# Tasks & Error Handling
-
-This example shows how to use `Task` with the [basic-cli platform](https://github.com/roc-lang/basic-cli). We'll explain how tasks work while demonstrating how to read command line arguments and environment variables, write files, and fetch content through HTTP.
-
-We recommend you read the [tasks and backpassings sections in the tutorial](https://www.roc-lang.org/tutorial#tasks) first and open up the [documentation for the basic-cli platform](https://www.roc-lang.org/packages/basic-cli/Task) on the side.
-
-Remember; a Task represents an [effect](https://en.wikipedia.org/wiki/Side_effect_(computer_science)); an interaction with state outside your Roc program, such as the terminal's standard output, or a file.
-
-Below we'll introduce the example code step by step, you can check out the full code at any time at the bottom.
-
-### main
-
-The [roc-lang/basic-cli](https://github.com/roc-lang/basic-cli) platform requires an application to provide a Task, namely `main : Task {} *`. This task usually represents a sequence or combination of `Tasks`, and will resolve to an empty record `{}`. This is similar to `void` or `unit` in other programming languages.
-
-The `main` task is run by the platform when the application is executed. It cannot return errors, which is indicated by the `*`.
-
-For this example, we'll be using the following main:
-```roc
-main : Task {} *
-main =
- run
- |> Task.onErr handleErr
-
-# Error : [ FailedToReadArgs, FailedToFetchHtml Str, ... ]
-
-handleErr : Error -> Task {} *
-
-run : Task {} Error
-```
-
-The `run : Task {} Error` task resolves to a success value of an empty record, and if it fails, returns with our custom `Error` type.
-
-This simplifies error handling so that a single `handleErr` function can be used to handle all the `Error` values that could occur.
-
-### run
-
-We want to see how fast our app runs, so we'll start our `run` `Task` by getting the current time.
-
-```roc
-startTime = Utc.now!
-```
-
-To get the current time, we need to interact with state outside of the roc program.
-We can not just calculate the current time, so we use a task, `Utc.now`.
-It's type is `Task Utc *`. The task resolves to the [UTC time](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) (since [Epoch](https://en.wikipedia.org/wiki/Unix_time)).
-
-#### Read an environment variable
-
-Next up in the task chain we'll read the environment variable `HELLO`:
-
-```roc
-helloEnvVar = readEnvVar! "HELLO"
-
-# …
-
-readEnvVar : Str -> Task Str *
-```
-And print it (to [stdout](https://en.wikipedia.org/wiki/Standard_streams)):
-
-```roc
-Stdout.line! "HELLO env var was set to $(helloEnvVar)"
-```
-
-### Command line arguments
-
-
-When reading command line arguments, it's nice to be able to read multiple arguments. We can use [record destructuring](https://www.roc-lang.org/tutorial#record-destructuring) to fit these multiple arguments nicely in our chain:
-
-```roc
-{ url, outputPath } = readArgs!
-
-# …
-
-readArgs : Task { url: Str, outputPath: Path } [FailedToReadArgs]_
-```
-
-Notice that `readArgs` can actually return an error unlike the previous tasks, namely `FailedToReadArgs`.
-By using `!` syntax to chain our tasks we can deal with errors at the end so it doesn't interrupt the flow of our code right now.
-
-The underscore (`_`) at the end of `[FailedToReadArgs]` is a temporary workaround for [an issue](https://github.com/roc-lang/roc/issues/5660).
-
-Note: running the formatter on the `readArgs` implementation currently results in a [parser issue](https://github.com/roc-lang/roc/issues/6074), so skip formatting as a temporary workaround until it's fixed.
-
-### Fetch website content
-
-We'll use the `url` we obtained in the previous step and retrieve its contents:
-
-```roc
-strHTML = fetchHtml! url
-```
-
-### Write to a file
-
-Next up, we'll write our strHTML to a file located at `outputPath`.
-
-```roc
-File.writeUtf8 outputPath strHTML
-|> Task.onErr! \_ -> Task.err (FailedToWriteFile outputPath)
-```
-
-The `File.writeUtf8` task resolves to an empty record if the provided `Str` is sucessfully written. The error type for `writeUtf8` is `[FileWriteErr Path WriteErr]` but we'd like to replace it with our own simpler error here. For that we use `Task.onErr`.
-
-### List the contents of a directory
-
-We're going to finish up with something more involved:
-
-```roc
-listCwdContent
-|> Task.map \dirContents ->
- List.map dirContents Path.display
- |> Str.joinWith ","
-
-|> Task.await! \contentsStr ->
- Stdout.line "Contents of current directory: $(contentsStr)"
-
-# …
-
-listCwdContent : Task (List Path) [FailedToListCwd]_
-```
-
-We call `listCwdContent` to list all files and folders in the current directory.
-Next, we take this list of paths, turn them all into `Str` using `Path.display`, and join/concatenate this list with a ",".
-
-We use `Task.map` to transform the success value of a `Task` into something that is not a `Task`, a `Str` in this case.
-
-Take a minute to look at the similarities and differences of `Task.map` and `Task.await`:
-
-```roc
-Task.map : Task a b, (a -> c) -> Task c b
-
-Task.await : Task a b, (a -> Task c b) -> Task c b
-```
-
-Next, we write our `Str` of combined `dirContents` to `Stdout`. We use `Task.await` because we're passing it a function that returns a `Task` with `Stdout.line`.
-
-### Feedback
-
-Tasks are important in roc, we'd love to hear how we can further improve this example. Get in touch on our [group chat](https://roc.zulipchat.com) or [create an issue](https://github.com/roc-lang/examples/issues).
-
-## Full Code
-
-```roc
-file:main.roc
-```
-
-## Output
-
-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 env var was set to 1
-Fetching content from https://www.roc-lang.org...
-Saving url HTML to roc.html...
-Contents of current directory: [...]
-Run time: 329 ms
-Done
-```
-
diff --git a/examples/Tasks/main.roc b/examples/Tasks/main.roc
deleted file mode 100644
index 73760a4..0000000
--- a/examples/Tasks/main.roc
+++ /dev/null
@@ -1,98 +0,0 @@
-app [main] {
- pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br",
-}
-
-import pf.Stdout
-import pf.Stderr
-import pf.Arg
-import pf.Env
-import pf.Http
-import pf.Dir
-import pf.Utc
-import pf.Path exposing [Path]
-
-main : Task {} _
-main = run |> Task.onErr handleErr
-
-run : Task {} _
-run =
-
- # Get time since [Unix Epoch](https://en.wikipedia.org/wiki/Unix_time)
- startTime = Utc.now! {}
-
- # Read the HELLO environment variable
- helloEnvVar =
- readEnvVar "HELLO"
- |> Task.map! \msg -> if Str.isEmpty msg then "was empty" else "was set to $(msg)"
- Stdout.line! "HELLO env var $(helloEnvVar)"
-
- # Read command line arguments
- { url, outputPath } = readArgs!
- Stdout.line! "Fetching content from $(url)..."
-
- # Fetch the provided url using HTTP
- strHTML = fetchHtml! url
- Stdout.line! "Saving url HTML to $(Path.display outputPath)..."
- # Write HTML string to a file
- Path.writeUtf8 strHTML outputPath
- |> Task.onErr! \_ -> Task.err (FailedToWriteFile outputPath)
- # Print contents of current working directory
- listCwdContent
- |> Task.map \dirContents ->
- List.map dirContents Path.display
- |> Str.joinWith ","
- |> Task.await! \contentsStr ->
- Stdout.line "Contents of current directory: $(contentsStr)"
-
- endTime = Utc.now! {}
- runTime = Utc.deltaAsMillis startTime endTime |> Num.toStr
- Stdout.line! "Run time: $(runTime) ms"
- # Final task doesn't need to be awaited
- Stdout.line! "Done"
-
-# NOTE in the future the trailing underscore `_` character will not be necessary.
-# This is a temporary workaround until [this issue](https://github.com/roc-lang/roc/issues/5660)
-# is resolved.
-
-readArgs : Task { url : Str, outputPath : Path } [FailedToReadArgs]_
-readArgs =
- when Arg.list! {} is
- [_, first, second, ..] ->
- Task.ok { url: first, outputPath: Path.fromStr second }
-
- _ ->
- Task.err FailedToReadArgs
-
-readEnvVar : Str -> Task Str []_
-readEnvVar = \envVarName ->
- when Env.var envVarName |> Task.result! is
- Ok envVarStr if !(Str.isEmpty envVarStr) ->
- Task.ok envVarStr
-
- _ ->
- Task.ok ""
-
-fetchHtml : Str -> Task Str [FailedToFetchHtml _]_
-fetchHtml = \url ->
- { Http.defaultRequest & url }
- |> Http.send
- |> Task.await \resp -> resp |> Http.handleStringResponse |> Task.fromResult
- |> Task.mapErr FailedToFetchHtml
-
-listCwdContent : Task (List Path) [FailedToListCwd]_
-listCwdContent =
- Dir.list "."
- |> Task.onErr \_ -> Task.err FailedToListCwd
-
-handleErr : _ -> Task {} _
-handleErr = \err ->
- usage = "HELLO=1 roc main.roc -- \"https://www.roc-lang.org\" roc.html"
-
- errorMsg =
- when err is
- FailedToReadArgs -> "Failed to read command line arguments, usage: $(usage)"
- FailedToFetchHtml httpErr -> "Failed to fetch URL $(Inspect.toStr httpErr), usage: $(usage)"
- FailedToWriteFile path -> "Failed to write to file $(Path.display path), usage: $(usage)"
- FailedToListCwd -> "Failed to list contents of current directory, usage: $(usage)"
- _ -> Inspect.toStr err
- Stderr.line! "Error: $(errorMsg)"
diff --git a/examples/TowersOfHanoi/Hanoi.roc b/examples/TowersOfHanoi/Hanoi.roc
index 744135c..1765c30 100644
--- a/examples/TowersOfHanoi/Hanoi.roc
+++ b/examples/TowersOfHanoi/Hanoi.roc
@@ -2,23 +2,23 @@ module [
hanoi,
]
+State : {
+ num_disks : U32, # number of disks in the Tower of Hanoi problem
+ 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
+}
+
## Solves the Tower of Hanoi problem using recursion. Returns a list of moves
## which represent the solution.
-hanoi :
- {
- numDisks : U32, # number of disks in the Tower of Hanoi problem
- 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
- }
- -> List (Str, Str)
-hanoi = \{ numDisks, from, to, using, moves } ->
- if numDisks == 1 then
+hanoi : State -> List (Str, Str)
+hanoi = \{ num_disks, from, to, using, moves } ->
+ if num_disks == 1 then
List.concat moves [(from, to)]
else
moves1 = hanoi {
- numDisks: (numDisks - 1),
+ num_disks: (num_disks - 1),
from,
to: using,
using: to,
@@ -28,20 +28,43 @@ hanoi = \{ numDisks, from, to, using, moves } ->
moves2 = List.concat moves1 [(from, to)]
hanoi {
- numDisks: (numDisks - 1),
+ num_disks: (num_disks - 1),
from: using,
to,
using: from,
moves: moves2,
}
-start = { numDisks: 0, from: "A", to: "B", using: "C", moves: [] }
+start = { num_disks: 0, from: "A", to: "B", using: "C", moves: [] }
## Test Case 1: Tower of Hanoi with 1 disk
-expect hanoi { start & numDisks: 1 } == [("A", "B")]
+expect
+ actual = hanoi { start & num_disks: 1 }
+ actual
+ == [
+ ("A", "B"),
+ ]
## Test Case 2: Tower of Hanoi with 2 disks
-expect hanoi { start & numDisks: 2 } == [("A", "C"), ("A", "B"), ("C", "B")]
+expect
+ actual = hanoi { start & num_disks: 2 }
+ actual
+ == [
+ ("A", "C"),
+ ("A", "B"),
+ ("C", "B"),
+ ]
## Test Case 3: Tower of Hanoi with 3 disks
-expect hanoi { start & numDisks: 3 } == [("A", "B"), ("A", "C"), ("B", "C"), ("A", "B"), ("C", "A"), ("C", "B"), ("A", "B")]
+expect
+ actual = hanoi { start & num_disks: 3 }
+ actual
+ == [
+ ("A", "B"),
+ ("A", "C"),
+ ("B", "C"),
+ ("A", "B"),
+ ("C", "A"),
+ ("C", "B"),
+ ("A", "B"),
+ ]
diff --git a/examples/Tuples/main.roc b/examples/Tuples/main.roc
index d91419f..5441bd7 100644
--- a/examples/Tuples/main.roc
+++ b/examples/Tuples/main.roc
@@ -1,40 +1,35 @@
-app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br" }
+app [main!] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.18.0/0APbwVN1_p1mJ96tXjaoiUCr8NBGamr8G8Ac_DrXR-o.tar.br" }
import pf.Stdout
-main =
+main! = \_args ->
# a tuple that contains different types
- simpleTuple : (Str, Bool, I64)
- simpleTuple = ("A String", Bool.true, 15_000_000)
+ 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)
- firstItem = simpleTuple.0
- secondItem = if simpleTuple.1 then "true" else "false"
- thirdItem = Num.toStr simpleTuple.2
- Stdout.line!
+ first_item = simple_tuple.0
+ second_item = if simple_tuple.1 then "true" else "false"
+ third_item = Num.toStr simple_tuple.2
+
+ try
+ Stdout.line!
"""
- First is: $(firstItem),
- Second is: $(secondItem),
- Third is: $(thirdItem).
+ First is: $(first_item),
+ Second is: $(second_item),
+ Third is: $(third_item).
"""
# You can also use tuples with `when`:
- fruitSelection : [Apple, Pear, Banana]
- fruitSelection = Pear
+ fruit_selection : [Apple, Pear, Banana]
+ fruit_selection = Pear
quantity = 12
- when (fruitSelection, quantity) is
+ 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 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."
diff --git a/examples/index.md b/examples/index.md
index bdcf752..de28145 100644
--- a/examples/index.md
+++ b/examples/index.md
@@ -9,8 +9,8 @@ You can find the source code for all of these at [github.com/roc-lang/examples](
- [Basic Dict Usage](/BasicDict/README.html)
- [Tuples](/Tuples/README.html)
- [Pattern Matching on Lists](/PatternMatching/README.html)
-- [Results & Error Handling](/Results/README.html)
-- [Tasks & Error Handling](/Tasks/README.html)
+- [Error Handling Basic (Result)](/Results/README.html)
+- [Error Handling Real World (Result, try)](/ErrorHandling/README.html)
- [Import Files](/IngestFiles/README.html)
- [Import from Directory](/ImportFromDirectory/README.html)
- [Import Package from Module](/ImportPackageFromModule/README.html)
@@ -24,10 +24,9 @@ You can find the source code for all of these at [github.com/roc-lang/examples](
- [Graph Traversal](/GraphTraversal/README.html)
- [Parser](/Parser/README.html)
- [Arithmetic](/Arithmetic/README.html)
-- [Looping Tasks](/TaskLoop/README.html)
+- [Looping Tasks](/LoopEffect/README.html)
- [Record Builder](/RecordBuilder/README.html)
- [Encoding & Decoding Abilities](/EncodeDecode/README.html)
-- [Desugaring !](/DesugaringAwait/README.html)
- [Desugaring ?](/DesugaringTry/README.html)
- [Custom Inspect](/CustomInspect/README.html)
- [Least Squares](/LeastSquares/README.html)
diff --git a/flake.lock b/flake.lock
index 09f6844..ef4af2b 100644
--- a/flake.lock
+++ b/flake.lock
@@ -3,11 +3,11 @@
"flake-compat": {
"flake": false,
"locked": {
- "lastModified": 1696426674,
- "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+ "lastModified": 1732722421,
+ "narHash": "sha256-HRJ/18p+WoXpWJkcdsk9St5ZiukCqSDgbOGFa8Okehg=",
"owner": "edolstra",
"repo": "flake-compat",
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "rev": "9ed2ac151eada2306ca8c418ebd97807bb08f6ac",
"type": "github"
},
"original": {
@@ -39,11 +39,11 @@
"systems": "systems_2"
},
"locked": {
- "lastModified": 1726560853,
- "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
- "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
@@ -79,17 +79,17 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1712163089,
- "narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=",
+ "lastModified": 1722403750,
+ "narHash": "sha256-tRmn6UiFAPX0m9G1AVcEPjWEOc9BtGsxGcs7Bz3MpsM=",
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5",
+ "rev": "184957277e885c06a505db112b35dfbec7c60494",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
- "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5",
+ "rev": "184957277e885c06a505db112b35dfbec7c60494",
"type": "github"
}
},
@@ -102,11 +102,11 @@
"rust-overlay": "rust-overlay"
},
"locked": {
- "lastModified": 1733120124,
- "narHash": "sha256-xqhX2erDfsjBrbT61LJzH124QtOf7/KZyTBpeJOBKas=",
+ "lastModified": 1735910140,
+ "narHash": "sha256-v1/8HGe29QO0M9SfZruAMvHgWgm3SJPg7GFEVrXzEPM=",
"owner": "roc-lang",
"repo": "roc",
- "rev": "a7168a4ad6ea9440bc8a8538672c633cf3a2261d",
+ "rev": "2263d8821a213629ee62193cdf56af9a2796c1c1",
"type": "github"
},
"original": {
@@ -133,11 +133,11 @@
]
},
"locked": {
- "lastModified": 1727490462,
- "narHash": "sha256-OrrPiNBiikv9BR464XTT75FzOq7tKAvMbMi7YOKVIeg=",
+ "lastModified": 1732802692,
+ "narHash": "sha256-kFrxb45qj52TT/OFUFyTdmvXkn/KXDUL0/DOtjHEQvs=",
"owner": "oxalica",
"repo": "rust-overlay",
- "rev": "11a13e50debafae4ae802f1d6b8585101516dd93",
+ "rev": "34971069ec33755b2adf2481851f66d8ec9a6bfa",
"type": "github"
},
"original": {