Prompted by the session yesterday, I made a cowsay clone, rocsay.roc. You'll need the template roc.txt too. Put both of these in examples/interactive
in the repo and run roc examples/interactive/rocsay.roc
from the repo root. Here's a screenshot in case you don't want to run it.
Does the CLI platform allow reading arguments yet? I wanted to make rocsay take a string, like cowsay, but couldn't find anything in the CLI patform in the repo about taking command line args. I know the platform's new so it might not be available yet, but I wanted to check anyway.
SO COOL! Yes, the example CLI platform should be getting arg parsing soon: https://github.com/roc-lang/roc/pull/3990
love it!!! :heart_eyes_cat:
#3990 has landed, so the CLI platform now takes a main
function that accepts command-line arguments!
We've also added an command-line argument parser to the CLI platform, that you can use for all your CLI parsing needs. There's an example at https://github.com/roc-lang/roc/blob/main/examples/interactive/args.roc. Feel free to send suggest/file issues for feature requests and bugs for the arg parser!
Updated! Haven't use the fancy arg parsing yet, but I'll add some flags next. Here's the new code & a screenshot!
Thanks so much for the work on the args :pray: Looking forward to more happy hacking in Roc!
I love seeing a one-day-old built-in (Str.replaceEach
) used for such fun
Hi, I don't know if it's the best place, or if I should create a new channel.
After testing the new "args" feature of the "cli-platform", I came across this behavior when sending bad arguments:
$ ./examples/interactive/args log --num 3
[...]
Argument `--num` is required but was not provided!
I thought it would be a good opportunity to get into the implementation a bit more, and try to fix it. But I'm a bit stuck with the expect
behavior that panics every time I want to do a failing test. And as I'm not very comfortable with the code yet, I'd like to do some changes in expect
s, for example, this will panic (from Arg.go:612):
# two string parsers complete cases
expect
parser =
succeed (\foo -> \bar -> "foo: \(foo) bar: \(bar)")
|> withParser (str { long: "foo" })
|> withParser (str { long: "bar" })
cases = [
["--foo", "true"]
]
List.all cases \args -> parseHelp parser args == Ok "foo: true bar: baz"
@Folkert de Vries for the above, it looks like the ExpectFrame
is being read at the wrong address and ends up corrupted - any ideas?
@Ghislain in the meantime you can do something like
# two string parsers complete cases
expect
parsed =
parser =
succeed (\foo -> \bar -> "foo: \(foo) bar: \(bar)")
|> withParser (str { long: "foo" })
|> withParser (str { long: "bar" })
parseHelp parser ["--foo", "true"]
parsed == Ok "foo: true bar: baz"
however you will notice that the error message is incorrect - it prints out the wrong error value :sweat_smile: so there is some reading of the wrong bytes there too!
can we get that snippet down to a self-contained example?
the failing one, I mean
@Ayaz Hafiz I got it down to
Parser a := [
Succeed a,
Arg Config (List Str -> Result a [NotFound, WrongType]),
# TODO: hiding the record behind an alias currently causes a panic
SubCommand
(List {
name : Str,
parser : Parser a,
}),
# Constructed during transformations of the above variants
WithConfig (Parser a) Config,
Lazy ({} -> a),
]
andMap : Parser a, Parser (a -> b) -> Parser b
andMap = \@Parser parser, @Parser mapper ->
unwrapped =
when mapper is
Succeed fn ->
when parser is
Succeed a ->
Lazy \{} -> fn a
Lazy thunk ->
Lazy \{} -> fn (thunk {})
WithConfig parser2 config ->
parser2
|> andMap (@Parser mapper)
|> WithConfig config
Arg config run ->
Arg config \args ->
run args
|> Result.map fn
SubCommand cmds ->
mapSubParser = \{ name, parser: parser2 } ->
{ name, parser: andMap parser2 (@Parser mapper) }
List.map cmds mapSubParser
|> SubCommand
Arg config run ->
when parser is
Succeed a ->
Arg config \args ->
when run args is
Ok fn -> Ok (fn a)
Err err -> Err err
Lazy thunk ->
Arg config \args ->
when run args is
Ok fn -> Ok (fn (thunk {}))
Err err -> Err err
WithConfig parser2 config2 ->
parser2
|> andMap (@Parser mapper)
|> WithConfig config2
Arg config2 run2 ->
# Parse first the one and then the other.
combinedParser = Arg config2 \args ->
when run args is
Ok fn -> run2 args |> Result.map fn
Err err -> Err err
# Store the extra config.
@Parser combinedParser
|> WithConfig config
SubCommand cmds ->
# For each subcommand, first run the subcommand, then
# push the result through the arg parser.
mapSubParser = \{ name, parser: parser2 } ->
{ name, parser: andMap parser2 (@Parser mapper) }
List.map cmds mapSubParser
|> SubCommand
Lazy thunk ->
fn = thunk {}
when parser is
Succeed a ->
Lazy \{} -> fn a
Lazy innerThunk ->
Lazy \{} -> fn (innerThunk {})
WithConfig parser2 config ->
parser2
|> andMap (@Parser mapper)
|> WithConfig config
Arg config run ->
Arg config \args ->
run args
|> Result.map fn
SubCommand cmds ->
mapSubParser = \{ name, parser: parser2 } ->
{ name, parser: andMap parser2 (@Parser mapper) }
List.map cmds mapSubParser
|> SubCommand
WithConfig mapper2 config ->
@Parser parser
|> andMap mapper2
|> WithConfig config
SubCommand cmds ->
mapSubParser = \{ name, parser: mapper2 } ->
{ name, parser: andMap (@Parser parser) mapper2 }
List.map cmds mapSubParser
|> SubCommand
@Parser unwrapped
withParser = \arg1, arg2 -> andMap arg2 arg1
succeed : a -> Parser a
succeed = \val -> @Parser (Succeed val)
# two string parsers complete cases
expect
parser =
succeed (\foo -> \bar -> "foo: \(foo) bar: \(bar)")
|> withParser (str { long: "foo" })
|> withParser (str { long: "bar" })
cases = [
["--foo", "true"]
]
List.all cases \args -> parseHelp parser args == Ok "foo: true bar: baz"
but when running that with roc test
I get
── EXPECT PANICKED ──────────────────────── examples/benchmarks/Scratchpad.roc ─
This expectation crashed while running:
137│> # two string parsers complete cases
138│> expect
139│> parser =
140│> succeed (\foo -> \bar -> "foo: \(foo) bar: \(bar)")
141│> |> withParser (str { long: "foo" })
142│> |> withParser (str { long: "bar" })
143│>
144│> cases = [
145│> ["--foo", "true"]
146│> ]
147│>
148│> List.all cases \args -> parseHelp parser args == Ok "foo: true bar: baz"
The crash reported this message:
Hit an erroneous type when creating a layout for `#UserApp.succeed`
so what we're seeing may be some interaction between type errors and the expect serialization not working in that case
with the full file, there are no type errors but expect still fails. I'll try to cut down a minimal reproduction
@Ghislain that bug should be fixed on the main
branch now!
Are positional arguments not yet supported?
For example: ./myApp echo 'hello world'
instead of ./myApp echo --text 'hello world'
They are not yet supported
Ghislain said:
...
After testing the new "args" feature of the "cli-platform", I came across this behavior when sending bad arguments:
...
Good news - @Ayaz Hafiz seems to have fixed that!
@rocco issue 4129
https://github.com/roc-lang/roc/issues/4129
Last updated: Jul 06 2025 at 12:14 UTC