Stream: ideas

Topic: basic-cli Cmd api


view this post on Zulip Luke Boswell (Apr 16 2024 at 03:48):

After writing out some script stuff, I'm wondering if we should change the API for basic-cli Cmd, and combine the args into the new call. We would get rid of the Cmd.arg and Cmd.args functions as they wouldn't be needed.

# how about this
Cmd.new ["roc", "www/main.roc", "--", "www/content/", "www/dist/"]

# instead of this
Cmd.new "roc"
|> Cmd.args ["www/main.roc", "--", "www/content/", "www/dist/"]

Is there any reason we would want to keep these separate instead?

Maybe we have a Cmd that we later apply new arguments to before executing? but in this case is it really that much worse than just building from scratch?

view this post on Zulip Richard Feldman (Apr 16 2024 at 03:52):

I like that idea! :thumbs_up:

view this post on Zulip Brendan Hansknecht (Apr 16 2024 at 04:21):

The reason they are separate in rust is to make it easier to build up programmatically with constants and some conditionals. Instead of requiring building the list fully first.

view this post on Zulip Brendan Hansknecht (Apr 16 2024 at 04:22):

Don't have a major preference either way, just noting the logic

view this post on Zulip Richard Feldman (Apr 16 2024 at 04:35):

yeah I just kinda figure you could build up the list the same way first haha

view this post on Zulip Brendan Hansknecht (Apr 16 2024 at 04:48):

For sure. I wonder if there is any other benefit I am missing. Probably not, just a nice builder API.

view this post on Zulip Brendan Hansknecht (Apr 16 2024 at 04:49):

Oh, I guess if you ever expand part just args, there might be reason to build it on the command struct

view this post on Zulip Brendan Hansknecht (Apr 16 2024 at 04:49):

Like env and what not

view this post on Zulip Brendan Hansknecht (Apr 16 2024 at 04:49):

Instead of building n separate lists

view this post on Zulip Luke Boswell (Apr 16 2024 at 05:41):

Does this look ok? We introduce a possible invalid state where an empty list is passed in.

I think this is acceptable, as it does nothing, but I guess we could crash.

new : List Str -> Cmd
new = \programAndArgs ->
    (program, args) =
        when programAndArgs is
            [] -> ("",[])
            [p] -> (p,[])
            [p,.. as a] -> (p,a)

    @Cmd {
        program,
        args,
        envs: [],
        clearEnvs: Bool.false,
    }

view this post on Zulip Folkert de Vries (Apr 16 2024 at 07:24):

does an empty program name make any sense?

view this post on Zulip Folkert de Vries (Apr 16 2024 at 07:24):

that must actually be the reason right for the rust api: that you must specify a program name

view this post on Zulip Folkert de Vries (Apr 16 2024 at 07:25):

of course I could also specify the program name as "flkjf3rkejfkefej" and that would fail because it does not exist, but at least taking the program name separately is more explicit

view this post on Zulip Richard Feldman (Apr 16 2024 at 11:43):

well you can already pass in "" for the program name, so I'm not sure that's significantly better haha

view this post on Zulip Folkert de Vries (Apr 16 2024 at 11:51):

that would look weird

view this post on Zulip Folkert de Vries (Apr 16 2024 at 11:51):

while giving a program an empty list of arguments is fine

view this post on Zulip Richard Feldman (Apr 16 2024 at 12:22):

that's fair

view this post on Zulip Isaac Van Doren (Apr 16 2024 at 13:11):

How about making new accept a Str for the program name and a separate list of args? That way it would be more clear that the program jam is required

view this post on Zulip Luke Boswell (Apr 17 2024 at 11:10):

After thinking on this, I think we should stick with the current API for a little longer.

With the new syntax, it will be nicer I think to write scripts, and so we should have more real experiences to work with.

I'd like to revisit this idea in a few months.

view this post on Zulip witoldsz (Apr 17 2024 at 12:41):

I was playing a little and came up with:

cmd = \app, args, errMapper ->
    Cmd.new app |> Cmd.args args |> Cmd.status |> Task.mapErr errMapper

removeDir = \path ->
    verifyDirExists! path
    line! "Removing files from $(path)..."
    cmd "rm" ["-rf", path] ErrRemovingDir

copyFiles = \from, to ->
    verifyDirExists! from
    line! "Copying files from $(from) to $(to)..."
    cmd "cp" ["-r", from, to] ErrCopyingFiles

generateSiteContent = \path ->
    verifyDirExists! path
    line! "Generating static site..."
    cmd "roc" ["www/main.roc", "--", "www/content/", "www/dist/"] ErrBuildingSite

serveFiles =
    line! "Serving static site..."
    cmd "simple-http-server" ["-p", "8080", "--nocache", "--cors", "--index", "--", "www/dist/"] ErrServingFiles

I think it is good enough at this point not to change the official API. It's just a one/two lines and everyone can adjust to their own requirements on the spot.

view this post on Zulip Richard Feldman (Apr 17 2024 at 12:50):

I'm actually in favor of changing it :big_smile:

view this post on Zulip Richard Feldman (Apr 17 2024 at 12:50):

the API we're using is actually pretty unusual for a language like Roc

view this post on Zulip Richard Feldman (Apr 17 2024 at 12:51):

the List Str approach is much more common, and I think the Rust API that ours is based on is kind of an outlier

view this post on Zulip Richard Feldman (Apr 17 2024 at 12:51):

I think it's worth switching to try doing it the more common way!

view this post on Zulip witoldsz (Apr 17 2024 at 13:01):

I am still a little bit confused: does the Cmd API belong to basic-cli just for now, or is it going to stay that way? In other words, do we want to create a standard for the CLI-like platforms?

view this post on Zulip Brendan Hansknecht (Apr 17 2024 at 13:03):

Once module params land, it could become generic and shared, but I assume it will stick in just basic CLI for a while.

view this post on Zulip Luke Boswell (Apr 17 2024 at 19:13):

We haven't been taling about envrionment variables, or the other configuration options though.

Another idea I had was accepting a Str for the program name, and then a record with the rest of the configuration options.

view this post on Zulip Brendan Hansknecht (Apr 17 2024 at 19:14):

That sound really nice with optional params

view this post on Zulip Richard Feldman (Apr 22 2024 at 00:49):

personally I think for the shell scripting use case (e.g. rewriting our build script for roc-lang.org from Bash to Roc), it would be very convenient to have something like:

exec : Str, List Str -> Task Output ExecErr

execWith : Str, { args ? List Str, ...etc } -> Task Output ExecErr

view this post on Zulip Luke Boswell (Apr 22 2024 at 01:27):

So there is a key difference between output and status.

As a helper for scripts I have implement the API as Cmd.exec : Str, List Str -> Task {} [Error] in #184 which behaves the same as status and inherits from the process executing the roc script.

Happy to change it again in a later PR.


Last updated: Jun 16 2026 at 16:19 UTC