Stream: ideas

Topic: Roc for scripting, minor annoyance


view this post on Zulip Kilian Vounckx (Dec 23 2024 at 09:53):

When using roc as a shell scripting language with shebang (#!/usr/bin/env roc) at the top, passing args is annoying because the args aren't passed to the script, but to the roc executable. The workaround I have used is to do #!/usr/bin/env -S roc --, so all args are passed to the script, but it would be nice to not have to do this.

A second minor annoyance is that when I create a script, I like to leave off file extensions. But roc automatically compiles to a file without extension, so it gets overwritten

view this post on Zulip Anton (Dec 23 2024 at 10:52):

I made #7405 for the shebang issue

view this post on Zulip Anton (Dec 23 2024 at 10:56):

so it gets overwritten

Right... should we error if we see that there is already a non-executable file with the name we want to use?

view this post on Zulip Brendan Hansknecht (Dec 23 2024 at 15:40):

Kilian Vounckx said:

When using roc as a shell scripting language with shebang (#!/usr/bin/env roc) at the top, passing args is annoying because the args aren't passed to the script, but to the roc executable.

Is this an issue with roc? all programming languages that can be used for scripting? a limitation of env?

Feels like we wouldn't be doing anything specific to make this worse.

view this post on Zulip Brendan Hansknecht (Dec 23 2024 at 15:42):

Anton said:

so it gets overwritten

Right... should we error if we see that there is already a non-executable file with the name we want to use?

When running a script, we should never emit an executable. It should either go to a cache directory or stay completely in memory.

Only with build should we emit an executable. We probably should follow other languages and emit it in a subdirectory of some sort. (Eventually we will need a subdirectory for caching anyway)

view this post on Zulip Kilian Vounckx (Dec 23 2024 at 15:43):

I think it's a problem with env yeah. As far as I know, the only thing roc could do is not have the need for -- between roc args and script args, but I like it when using the commands explicitly.
That's why it's a minor annoyance :big_smile:

view this post on Zulip Brendan Hansknecht (Dec 23 2024 at 15:44):

Ah, we have to be like cargo and if we don't recognize an arg, pass it to the child process

view this post on Zulip Brendan Hansknecht (Dec 23 2024 at 15:44):

Or something like that

view this post on Zulip Richard Feldman (Dec 23 2024 at 16:34):

yeah we used to do something like that, but the concern was that it could lead to surprising behavior, e.g. a script that works fine but then we release a new version of roc which introduces a new cli flag, and now roc eats it and the old script suddenly breaks even though it was a minor release ofroc :sweat_smile:

view this post on Zulip Richard Feldman (Dec 23 2024 at 16:35):

I like that the workaround is maximally reliable

view this post on Zulip Sam Mohr (Dec 23 2024 at 16:42):

Consistency should trump something nice

view this post on Zulip Jasper Woudenberg (Dec 23 2024 at 16:44):

I've ran into the same problem. I do agree many other programming languages seem to have the same problem, though I don't think it's all of them. With bash for instance, I never remember running into this problem.

I did a quick experiment with a short bash script that prints the arguments that get passed to it. Here's two invocations of it, both passing --version, an arg that bash itself parses:

$ bash ./foo.sh 1 2 3 --version                                                                           \jj/
./foo.sh 1 2 3 --version

$ bash --version ./foo.sh 1 2 3 --version
GNU bash, version 5.2.37(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

So what bash seems to do is that it doesn't parse any args past the file path passed to it for execution. Maybe that would be an option for Roc as well? That approach will not result in broken scripts if Roc introduces a new flag.

view this post on Zulip Sam Mohr (Dec 23 2024 at 16:46):

What if the file doesn't end in .sh

view this post on Zulip Jasper Woudenberg (Dec 23 2024 at 16:46):

I just checked, same behavior.

view this post on Zulip Jasper Woudenberg (Dec 23 2024 at 16:49):

I guess the downside of this approach is that you will no longer be able to invoice a script with roc shebang directly and pass arguments for Roc itself. But maybe that's okay? Maybe this is wrong, but I'd expect there to be less of a need to pass arguments to Roc itself when running a script directly instead of building a binary. And if you really need to pass an argument to Roc, there's still the possibility to call roc explicitly.

view this post on Zulip Sam Mohr (Dec 23 2024 at 16:53):

That probably won't work if you run a script called check without a roc extension

view this post on Zulip Sam Mohr (Dec 23 2024 at 16:53):

Or version or any of our subcommands that are words

view this post on Zulip Sam Mohr (Dec 23 2024 at 16:53):

Bash doesn't have that

view this post on Zulip Richard Feldman (Dec 23 2024 at 16:54):

would they come through as full file paths or relative? :thinking:

view this post on Zulip Richard Feldman (Dec 23 2024 at 16:54):

e.g. would we see check or ./check or something like that

view this post on Zulip Sam Mohr (Dec 23 2024 at 16:58):

I think if it's in the same folder, we'd get just check

view this post on Zulip Sam Mohr (Dec 23 2024 at 16:58):

I'm on mobile, so I can't check

view this post on Zulip Richard Feldman (Dec 23 2024 at 17:21):

same :laughing:

view this post on Zulip Sam Mohr (Dec 23 2024 at 17:29):

Let me grab a laptop...

view this post on Zulip Sam Mohr (Dec 23 2024 at 17:42):

A file named check in /home/smores/dev/:

#!/bin/bash

echo $0
echo $@

When I run it:

~/dev 21s
❯ bash check 123 456
check
123 456

~/dev
❯ ./check 123 456
./check
123 456

view this post on Zulip Sam Mohr (Dec 23 2024 at 17:44):

So to translate, if we ran a Roc script called check with roc then it would be kinda hard for it to run as check with roc dev check

view this post on Zulip Sam Mohr (Dec 23 2024 at 17:45):

But roc check would say something like "main.roc is missing"

view this post on Zulip Sam Mohr (Dec 23 2024 at 17:46):

So I think for shebangs, we could just go for the "if the arg is not a flag or a known subcommand, pass all the args to the script" route

view this post on Zulip Sam Mohr (Dec 23 2024 at 17:47):

Since with a shebang it'll be pretty hard to run a file without a relative path

view this post on Zulip Richard Feldman (Dec 23 2024 at 18:09):

hm wait but I think this confirms what we want, right?

view this post on Zulip Richard Feldman (Dec 23 2024 at 18:10):

because if we had #!/usr/bin/env roc at the top, and then ran it with ./check, roc would receive the string "./check" rather than "check"

view this post on Zulip Richard Feldman (Dec 23 2024 at 18:11):

if I understand the implications of this correctly, I think it means when you're running the roc script using #!/usr/bin/env roc, the first argument will always be a path that begins with either / or .

view this post on Zulip Richard Feldman (Dec 23 2024 at 18:11):

which in turn means it would never collide with a subcommand, and we could always detect that case and do the correct thing

view this post on Zulip Sam Mohr (Dec 23 2024 at 18:51):

Yep, we're good

view this post on Zulip Sam Mohr (Dec 23 2024 at 18:51):

The only problem would be a non-shebang use case

view this post on Zulip Ayaz Hafiz (Dec 23 2024 at 18:53):

You don't have to run it with a leading . or / though, you could just run check

view this post on Zulip Sam Mohr (Dec 23 2024 at 18:53):

If you have a Roc file without a file extension in the current dir and you want to run it with the dev backend, you have to run roc ./check

view this post on Zulip Sam Mohr (Dec 23 2024 at 18:53):

Oh yeah, if . is in PATH

view this post on Zulip Sam Mohr (Dec 23 2024 at 18:55):

Seems like there are gonna be tradeoffs

view this post on Zulip Sam Mohr (Dec 23 2024 at 18:56):

So might as well go for the option that's nice 99% of the time that lets you do ./check as a workaround

view this post on Zulip Richard Feldman (Dec 23 2024 at 18:56):

hm yeah I guess if it's in the PATH, that wouldn't work

view this post on Zulip Richard Feldman (Dec 23 2024 at 18:56):

I think that's the only scenario where it could come in as check

view this post on Zulip Sam Mohr (Dec 23 2024 at 18:56):

In fact, you can always do "$ROC ./$SCRIPT"

view this post on Zulip Sam Mohr (Dec 23 2024 at 18:57):

And it'll canonicalize

view this post on Zulip Richard Feldman (Dec 23 2024 at 18:59):

hm, at least as of 2014 some people were reporting that env wouldn't pass through more than one argument: https://stackoverflow.com/questions/16365130/what-is-the-difference-between-usr-bin-env-bash-and-usr-bin-bash#16365367

view this post on Zulip Richard Feldman (Dec 23 2024 at 19:00):

I'm not sure if that's still a thing though...I thought we'd determined at some point that all modern env implementations support passing through multiple arguments

view this post on Zulip Richard Feldman (Dec 23 2024 at 19:03):

all that said, I think the PATH consideration is an important one, because a major reason you might want to do the #! in the first place is that you have something on your system that's going to run a program on the path and you want to seamlessly replace that program with a roc script

view this post on Zulip Sam Mohr (Dec 23 2024 at 21:07):

You're right, but I think it's not generally considered good practice to put the current directory . in the PATH unless you're in some kind of virtual environment, and even then you probably have your scripts in a folder.

view this post on Zulip Sam Mohr (Dec 23 2024 at 21:08):

So almost all of those cases are probably running with relative/absolute paths anyway

view this post on Zulip Richard Feldman (Dec 23 2024 at 21:11):

well like I mean if I'm at the command line and I run roc, for example, if that resolved to a text file with #! at the top, I assume it would receive roc with no . or / in it

view this post on Zulip Sam Mohr (Dec 23 2024 at 21:14):

I'm not sure I follow. Are you talking about a behavior where roc run sans arguments would look for a local file?

view this post on Zulip Richard Feldman (Dec 23 2024 at 21:24):

so let's say I'm at the command line and I type foo and press enter

view this post on Zulip Richard Feldman (Dec 23 2024 at 21:25):

the shell is going to look in the PATH for something named foo

view this post on Zulip Richard Feldman (Dec 23 2024 at 21:25):

let's say foo turns out to be a text file that begins with #!/usr/bin/env roc

view this post on Zulip Richard Feldman (Dec 23 2024 at 21:25):

in that scenario, I think roc is going to receive foo and not ./foo or /path/to/foo or anything like that

view this post on Zulip Richard Feldman (Dec 23 2024 at 21:26):

and the point is that I think this is one of the important scenarios for #!/usr/bin/env roc to facilitate - where you have some (possibly thirtd-party) script that's going to execute foo from the PATH

view this post on Zulip Richard Feldman (Dec 23 2024 at 21:26):

and you want foo to run your roc program

view this post on Zulip Sam Mohr (Dec 23 2024 at 21:55):

I'll test that

view this post on Zulip Sam Mohr (Dec 23 2024 at 22:01):

~/dev
❯ cat /usr/bin/args-test
#!/usr/bin/env python3

import sys

print(f"Hi! The args are: {sys.argv}")

~/dev
❯ args-test
Hi! The args are: ['/usr/bin/args-test']

view this post on Zulip Sam Mohr (Dec 23 2024 at 22:01):

Seems like we're good?

view this post on Zulip Sam Mohr (Dec 23 2024 at 22:04):

This is on latest Pop OS on my Tuxedo laptop

view this post on Zulip Sam Mohr (Dec 23 2024 at 22:04):

Which is Ubuntu derivative

view this post on Zulip Jasper Woudenberg (Dec 23 2024 at 22:04):

I just ran something similar on NixOS, seeing the same thing. Looks like env passes on an absolute path. Convenient!

It'd be nice to see if it's the same on MacOS, just to make sure different env implementations don't mess it up or anything.

view this post on Zulip Jasper Woudenberg (Dec 23 2024 at 22:07):

Correction, it doesn't appear to be env turning the file into an absolute path, but rather the shebang magic. When I run env by hand it doesn't modify the command to an absolute path.

view this post on Zulip Sam Mohr (Dec 23 2024 at 22:10):

Probably not something we need to worry about, but good to know!

view this post on Zulip Richard Feldman (Dec 23 2024 at 22:26):

yeah works on macOS!

view this post on Zulip Richard Feldman (Dec 23 2024 at 22:35):

ok so that means as long as roc doesn't have any subcommands or flags that start with . or / (which of course would not happen), then we can have the rule of "all subcommands and flags must come before the filename, and as soon as we encounter something we think is a filename, everything after it gets forwarded as args to the Roc program"

view this post on Zulip Richard Feldman (Dec 23 2024 at 22:35):

and that would look fine except in the specific case where you were trying to do something like roc check to run a Roc script named check in the current directory, in which case don't do that :stuck_out_tongue:

view this post on Zulip Sam Mohr (Dec 23 2024 at 22:49):

Huh, ran into a bug while trying to test this: I made a file called roc-args-test and ran it, and roc built a binary over the file. It ran one time and then died forever :laughing:

view this post on Zulip Sam Mohr (Dec 23 2024 at 22:50):

Anyway, I'll double check but it gave just the file name

view this post on Zulip Sam Mohr (Dec 23 2024 at 22:58):

Yep, so I'm typing this on my phone with a laptop in the car...

view this post on Zulip Sam Mohr (Dec 23 2024 at 23:01):

#!/usr/bin/env roc
app [main] { pf: platform "basic-cli 0.17 URL" }

import pf.Arg
import pf.Stdout

main =
    Task.await (Arg.list {}) \args ->
        Stdout.line "Args are: $(Inspect.toStr args)"

view this post on Zulip Sam Mohr (Dec 23 2024 at 23:02):

> roc roc-args-test
Args are: ["roc-args-test"]

view this post on Zulip Sam Mohr (Dec 23 2024 at 23:02):

So if the file doesn't have a shebang and no extension, we show an error instead of running the app

view this post on Zulip Sam Mohr (Dec 23 2024 at 23:03):

If it has a shebang and no extension, we run it

view this post on Zulip Sam Mohr (Dec 23 2024 at 23:04):

So your expectations matched reality

view this post on Zulip Sam Mohr (Dec 23 2024 at 23:05):

So no need for a new GH issue on this

view this post on Zulip Richard Feldman (Dec 23 2024 at 23:09):

ok, so all we need to do is to change how we parse args in the roc cli?

view this post on Zulip Sam Mohr (Dec 23 2024 at 23:21):

We're gonna do that when we remove dev I presume, but yes

view this post on Zulip Richard Feldman (Dec 23 2024 at 23:23):

cool, sounds good to me!


Last updated: Jun 16 2026 at 16:19 UTC