Can someone please help me with a simple task? I'm trying to write a program that takes a list of integers on the command line, and prints their sum. Something roughly equivalent to this Python code:
import sys
print(sum([int(arg) for arg in sys.argv[1:]]))
I wrote the following program, but Roc complains about the type of parseNumber, and I'm lost.
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
import pf.Stdout
import pf.Task
import pf.Arg
parseNumber = \arg ->
when Str.toU8 arg is
Ok number -> Task.ok number
Err msg -> Task.err (Exit 1 "Invalid argument \"${arg}\": ${msg}")
parseNumbers = \args ->
args |> List.dropFirst 1 |> List.map parseNumber!
main =
numbers = Arg.list! {} |> parseNumbers!
total = List.sum numbers
Stdout.line! "Sum of numbers: $(Num.toStr total)"
Thanks!
Here's one way to do it
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }
import pf.Stdout
import pf.Task
import pf.Arg
parseNumbers : List Str, List U8 -> Result (List U8) [InvalidArgument Str]
parseNumbers = \args, acc ->
when args is
[] -> Ok acc
[first, .. as rest] ->
when Str.toU8 first is
Ok number -> parseNumbers rest (List.append acc number)
Err InvalidNumStr -> Err (InvalidArgument "Got invalid argument $(first)")
expect parseNumbers ["1", "2", "3"] [] == Ok [1, 2, 3]
expect parseNumbers ["1", "2", "a"] [] |> Result.isErr
main =
args = Arg.list! {}
numbers =
args
|> List.dropFirst 1
|> parseNumbers []
|> Task.fromResult!
total = List.sum numbers
Stdout.line! "Sum of numbers: $(Num.toStr total)"
edit: added the drop first arg which is the program name
$ roc parseNumbers.roc -- 1 2 3 4
Sum of numbers: 10
This is a slightly more complicated to support the early return with an error.
If you are happy just mapping the argument List Str
into a List U8
and only keeping the valid ones its easier.
args =
Arg.list {}
|> Task.map \argStrs -> argStrs |> List.keepOks Str.toU8
I think that should be Str.toUtf8
at the end there (not toU8)
I think Str.toU8 is the right idea, but probably would be better to use a larger integer type
In context it could be like this
main =
args = Arg.list! {}
total =
args
|> List.dropFirst 1
|> List.keepOks Str.toI64
|> List.sum
Stdout.line "The sum is $(total |> Num.toStr)"
Thanks guys, that's very helpful! :big_smile:
My plan is to solve Advent-of-Code 2020 using Roc, so the numbers I'm trying to parse are actually days, from 1 to 25, hence the U8
. I don't actually need to compute their sum, but it was simpler to explain.
In case you're interested in the preliminary impressions of a complete newbie (just as a data point), here are my thoughts so far:
Arg.list!
to work (I had to add {}
and change the basic-cli version).Task
/ Result
interaction.List.
all the time, perhaps a bit more syntactic sugar might be nice.Thank you for the feedback, this is very helpful.
I wish I could hover over a def in VSCode and get its type.
You should be able to using the LS and VScode extension from @Ivan Demchenko which I just installed from the marketplace.
Here's a demo
Screenshot-2024-07-29-at-15.21.08.png
I don't think I had to do anything other than set the Path to the LS executable in the extension settings
- :cross_mark: The language seems very verbose
I think this is very dependent on author and target audience. For example, more compact forms of writing may be hard to follow as a beginner so they might not get written as often here.
That said:
I 100% agree the language is more verbose... but I don't think this is a bad thing either. I also had the same first impression, though it's hard for me to be unbiased now I've spent so much time writing roc.
One thing I've noticed from doing AoC and reading other people's solutions is that mine are generally 2-4x longer. I find some people are exceptionally good at writing much shorter solutions than I.
I often enjoy polishing my code, and playing a bit of golf, but I still find myself writing in a much more verbose style. I'm not sure why this is, but I find it interesting to think about.
Taking parseNumbers
for example, it could be written as:
parseNumbers : List Str-> Result (List U8) [InvalidNumStr]
parseNumbers = \args ->
List.mapTry Str.toU8
Though if you want the same error type, it would be:
parseNumbers : List Str-> Result (List U8) [InvalidArgument Str]
parseNumbers = \args ->
List.mapTry \str ->
Str.toU8 str |> Result.mapErr (\_ -> InvalidArgument str)
I'm already a bit tired of typing
List.
all the time
I'm really looking forward to one day having editor tooling that can autodetect imports from the Builtins. So I could just write toU8
and the editor might auto add List.
prefix and also import it for me.
I think it's something we've discussed before.
List.mapTry Str.toU8Checked
Oh nice. I hadn't thought of that.
This whole thing could be (assuming desugaring of !
works correctly, might not yet):
main =
Arg.list! {}
|> List.dropFirst 1
|> List.mapTry Str.toU8
|> Task.fromResult!
|> List.sum
|> \total -> "Sum of numbers: $(Num.toStr total)"
|> Stdout.line!
Though maybe that is too much |>
...haha
such as a function to run multiple tasks sequentially and return the first error if any
Just a note, this actually does exist: https://www.roc-lang.org/packages/basic-cli/0.12.0/Task#sequence
Thanks for the great feedback @Aurélien Geron!
The docs and examples are not fully in sync with the latest release. In particular, I struggled to get
Arg.list!
to work
I'd like to fix this, can you tell me where to find these outdated docs/examples?
The ones I looked at are up to date but I must have missed something
:cross_mark: The language seems very verbose
One thing that makes Roc more verbose is our encouragement of error handling. print(sum([int(arg) for arg in sys.argv[1:]]))
would crash if you provide an arg that is not an int but python does not encourage you to catch this.
In advent of code you just want to get things done quickly, but you want real world code to be reliable.
:cross_mark: The REPL doesn't feel very useful yet, since it doesn't have any effects.
Effects for the REPL are planned, anybody know if we have an issue for this?
print(sum([int(arg) for arg in sys.argv[1:]]))
would also return 0 if the user did not enter any args but that is not ideal behavior for real software. You probably want to present an error message to the user in this case.
anybody know if we have an issue for this?
I don't know if we do. But @Bram's work in https://roc.zulipchat.com/#narrow/stream/316715-contributing/topic/playground.2Frepl.20with.20LSP.20support/near/453747271 is very closely related I think. He's looking at the problem from the WASM perspective, but enabling effects in WASM is very similar to the REPL I'm guessing.
this should work, but doesn't because of a compiler bug (that we discussed elsewhere :sweat_smile:)
main =
Arg.list! {}
|> List.mapTry Str.toU8
|> Task.fromResult!
|> List.sum
|> Num.toStr
|> Stdout.line!
:cross_mark: The compiler's error messages are not always clear enough. For example, I really got 100% stuck on the simple problem above, despite my best efforts. Try running my code to see what I mean. Perhaps the tutorial needs more examples of
Task
/Result
interaction.
I feel like a good tip here would be to recommend adding type annotations, which is how I fixed the code. Should we add that as a tip to all these opaque type mismatches?
Just like a little addendum? "note: adding type annotations may help improve and narrow down this error message`
Anton said:
Thanks for the great feedback Aurélien Geron!
The docs and examples are not fully in sync with the latest release. In particular, I struggled to get
Arg.list!
to workI'd like to fix this, can you tell me where to find these outdated docs/examples?
Sorry for the late reply. I can't find any sync issues anymore, so I suppose I must have mixed up the versions I was looking at, perhaps using basic-cli
version 0.11 instead of 0.12. I'll let you know if I find anything, but for now this looks more like a brain fart on my part. :woozy_face:
Last updated: Jul 05 2025 at 12:14 UTC