Stream: ideas

Topic: A platform for production CLIs


view this post on Zulip jan kili (Dec 17 2021 at 20:20):

I started brainstorming in markdown :smiley:

# Rocli

## Potential goals

- Rocli is the best way to build modern CLI applications with the Roc programming language.
- Rocli is a modular CLI platform for Roc.

## Outline

- A Rust-based Roc platform
- Features:
    - I/O (stdin/out/err & files)
    - Complete error handling
    - Command parsing
    - Flag parsing
    - Argument parsing
- `requires`:
    - `main : Request -> Task {} []`
- `interface`s:
    - `File`
        - `exposes`:
            - `append : Str, Str -> Task {} [ FileNotFound, PermissionDenied ]`
            - `read : Str -> Task Str [ FileNotFound, PermissionDenied ]`
            - `write : Str, Str -> Task {} [ DirectoryNotFound, PermissionDenied ]`
    - `Parser`
        - `exposes`:
            - `appendHelpFlag : CommandSchema -> CommandSchema`
            - `ArgumentSchema : { name: Str, description: Str }`
            - `ArgumentValue : { contents: Str, schema: ArgumentSchema }`
            - `CommandSchema : { name: Str, shortName: Str, description: Str, flags: List FlagSchema, arguments: List ArgumentSchema }`
            - `CommandValue : { arguments: List Argument, flags: List Flag, schema: CommandSchema }`
            - `FlagSchema : { name: Str, isRequired: Bool, shortName: Str }`
            - `FlagValue : { contents: Str, wasUsed: Bool, schema: FlagSchema }`
            - `parseCommand : Request, List CommandSchema -> (Result CommandValue [ NoSchemaMatched ])`
    - `Request`
        - `exposes`:
            - `Request : { executablePath: Str, rawArguments: List Str }`
    - `Std`
        - `exposes`:
            - `readInLine : Task Str [ BrokenPipe ]`
            - `writeErr : Str -> Task {} [ BrokenPipe ]`
            - `writeErrLine : Str -> Task {} [ BrokenPipe ]`
            - `writeOut : Str -> Task {} [ BrokenPipe ]`
            - `writeOutLine : Str -> Task {} [ BrokenPipe ]`
    - `Task`
        - `exposes`:
            - `Task ok err : Effect.Effect (Result ok err)`
            - `attempt : Task a b, (Result a b -> Task c d) -> Task c d`
            - `await : Task a err, (a -> Task b err) -> Task b err`
            - `fail : err -> Task * err`
            - `map : Task a err, (a -> b) -> Task b err`
            - `onFail : Task ok a, (a -> Task ok b) -> Task ok b`
            - `succeed : val -> Task val *`

## TODO

- Subcommand parsing
- Man page generation
- Autocomplete for shell(s) (Bash, etc.)
- Config handling
- Network requests
- Alias handling
- Testing helper module
- Rocli CLI
    - Code generation
    - Testing helper commands?

view this post on Zulip jan kili (Dec 17 2021 at 20:21):

I'm imagining something that would be competitive with cobra and oclif, both of which have pleasant DX.

view this post on Zulip jan kili (Dec 17 2021 at 20:22):

What do y'all think of this feature set? What do y'all think of this approach to the interfaces?

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:22):

I really like rust's structopt or argh

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:22):

I think auto generating the help message is important, as well as bash completions

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:23):

cobra is a good example as well

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:23):

https://docs.rs/structopt/latest/structopt/

view this post on Zulip jan kili (Dec 17 2021 at 20:25):

Ah, so it's CLI codegen for Rust?

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:25):

the question is, could something like that just be a library built on top of a generic platform intended to run in a terminal?

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:25):

JanCVanB said:

Ah, so it's CLI codegen for Rust?

sort of, you mostly just define all the possible args and flags as a struct or nested enums and structs

view this post on Zulip jan kili (Dec 17 2021 at 20:26):

Yes, my first idea for Rocli (name TBD, or not lol) was to have it be a set of repos - one for the platform, one for a parser library, one for a codgen tool, etc.

view this post on Zulip jan kili (Dec 17 2021 at 20:26):

However, since a platform comes with interface exposures anyways, I'm not sure that splitting it up is actually valuable

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:27):

here's an example where I used structopt:
https://github.com/rvcas/checkers/blob/main/src/main.rs#L27

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:27):

roc itself uses clap

view this post on Zulip Lucas Rosa (Dec 17 2021 at 20:27):

structopt is built on top of clap

view this post on Zulip jan kili (Dec 17 2021 at 22:32):

Lucas Rosa said:

the question is, could something like that just be a library built on top of a generic platform intended to run in a terminal?

This is a great question. Because this project is initially "a production-grade version of examples/cli", I'm anchored to the term "CLI" as referring to something more than a simple executable. However, what is actually the correct term/pattern here?

view this post on Zulip jan kili (Dec 17 2021 at 22:36):

Should this be split into two projects, where one is a robust platform for creating executables* and the other is a robust library for creating advanced CLIs with flags, args, etc?
(* I can't decide what the best noun for this - executable? terminal-based app? minimal CLI?)

view this post on Zulip jan kili (Dec 17 2021 at 22:45):

I expect that...
a) even simple executables would benefit from some of the less-advanced features like arg parsing
(ex: ./my_executable ./path/to/my_input_file.txt)
b) some more-advanced features would be platform-dependent
(ex: the file I/O required for caching an API token in a config file)
c) there are not many other directions an executable/terminal-focused platform could go in, other than supporting more-advanced CLI features

view this post on Zulip jan kili (Dec 17 2021 at 22:47):

However, I suppose that it could be nice to separate the advanced features like command/arg/flag parsing so that they can be re-used in non-OS-terminal environments... though I can't think of any.

view this post on Zulip jan kili (Dec 17 2021 at 22:58):

Then again, any Roc-only interfaces could simply be imported in any module, regardless of platform, right?

view this post on Zulip jan kili (Dec 17 2021 at 22:58):

As you can see, I'm going back and forth on this :laughing:

view this post on Zulip jan kili (Dec 17 2021 at 22:59):

I should also state that I expect the Parser interface to be a Roc-only module, which doesn't use any of the platform's internals or Tasks.

view this post on Zulip jan kili (Dec 17 2021 at 23:00):

(same with all non-I/O modules)

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:05):

I'm also particularly interested in a platform for console / TUI apps - think terminal editors/etc. I'd like to rewrite this tool I've been working on in Roc: https://github.com/joshuawarner32/debug_buf_viewer (screenshot in this issue: https://github.com/rtfeldman/roc/pull/2213)

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:06):

I've been thinking of making a platform that exposes more or less the same interface the crossterm rust crate.

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:06):

This doesn't necessarily have to be the same platform as for other CLI apps, but maybe?

view this post on Zulip jan kili (Dec 17 2021 at 23:17):

Wow! Great idea, that's a perfect rebuttal to my (c) expectation above.

view this post on Zulip jan kili (Dec 17 2021 at 23:21):

Should all of the following 3 apps be served by the same platform+libraries repo?

  1. A simple executable that use stdin & stdout
  2. A complex multi-command CLI with its own config file
  3. A terminal-based text editor with syntax highlighting

view this post on Zulip jan kili (Dec 17 2021 at 23:24):

What I/O Tasks would be required for #3's cursor movement & cursor-aware text formatting? (ex: highlighting the currently-hovered line/word) Is that just stdin?

view this post on Zulip jan kili (Dec 17 2021 at 23:32):

More broad Roc question: Since a Roc app can only use one platform, does that mean that a production-grade CLI platform would need to support all possible forms of I/O? stdin/out/err, files, network (HTTP, TCP, UDP), ... what else? Does that scale well? Could unrelated helpful libraries for things like network request abstraction (ex: a high-level GraphQL client) be platform-agnostic, or would they need to be platform-specific?

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:33):

What I/O Tasks would be required for #3's cursor movement & cursor-aware text formatting?

TUIs are complicated and filled with historical baggage. :grimacing: stdin/stdout are definitely required and give you _most_ of what you need; but that's not all unfortunately. You also need access to some APIs that allow switching terminal modes ("raw mode") - and being able to intercept/handle SIGWINCH ("window size changed") is really useful.

Ideally, we could come up with an API that really cleanly abstracts all of this.

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:35):

Like, it'd be cool if the same platform interface could also be used to write apps that cross-compile into the browser, without also loading some big terminal emulator.

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:36):

That'd require encapsulating formatting and cursor movement commands, rather than having them be encoded on stdout.

view this post on Zulip jan kili (Dec 17 2021 at 23:39):

Hmmm, do you mean like abstracting the CLI platform's interface to be generic enough that other/complementary Roc platforms could provide the same interface for different underlying execution/interaction environments?

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:40):

Yeah; I want to write this TUI tool, and then be able to (later) embed it in a web page or something to make it accessible to more people

view this post on Zulip jan kili (Dec 17 2021 at 23:40):

Whoa

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:40):

Usually formatting is encoded as ANSI codes - e.g. \e[33m for "please switch to writing text in yellow" - but if we could just have then Task interface accept (Text, Formatting) output (where formatting is some tag union), that'd be ideal.

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:41):

Commands for cursor movement are encoded similarly.

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:44):

One thing that's _realy_ complicated about writing a "modern"/"real" TUI app is handling proper localization / unicode - and knowing whether a terminal will render 日本 as two character slots or four (or something else), is a bit of a crapshoot. Similar for things like arabic (right to left ordering).

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:45):

All of this is quite a challenging problem, much of which isn't used for like 80% of terminal apps.

view this post on Zulip jan kili (Dec 17 2021 at 23:45):

That sounds cool, but such an abstraction will probably add overhead/confusion for simple CLIs. Therefore, I imagine that an abstraction library might be the best solution for that:

CLI platform X --> CLI app A
CLI platform X --> Cross-env abstraction library Y --> Cross-env app B
Browser platform Z --> Cross-env abstraction library Y --> Cross-env app B? C?
Browser platform Z --> Browser app D

view this post on Zulip jan kili (Dec 17 2021 at 23:47):

(however, I don't know much about platforms, so I'm just guessing here)

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:50):

In terms of functionality that various apps need, I'd put things into four buckets:

  1. Apps that only ever append to stdout/stderr, with _zero_ ANSI codes. (no colors, no cursor movement, etc)
  2. Apps that only ever append to the stdout or stderr. They might optionally spit out things in various colors, but nothing else.
  3. Apps that do the above, but also go back and re-draw parts of the output. A common example is cargo, where it progress bars for all the different downloads/compilations going on at that time.
  4. "alternate screen + raw mode" apps, that take control over the whole terminal, implement custom handlers for e.g. ctrl+C/ctrl+Z/etc.

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:52):

  1. is distinct because it's the most suitable for e.g. being part of a unix pipeline
  2. is distinct because you could _probably_ just strip the formatting when it's part of a unix pipeline (many programs will detect when they're on a pipeline using some heuristics and disable colors automatically)
  3. is distinct because, at this point, there's really not much value you'd get out of trying to do something like grep the output. You're going to get garbage.
  4. is in a completely different world.

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:53):

1/2/3 _could_ be combined into one - as perhaps could 2/3/4 - but 1 & 4 should probably always remain separate.

view this post on Zulip Joshua Warner (Dec 17 2021 at 23:53):

But yes, I think you're 100% right in terms of building compatibility layers.

view this post on Zulip jan kili (Dec 18 2021 at 00:37):

@Joshua Warner For use cases 1, 2, and 3, what do you think of the above interface proposals?

view this post on Zulip jan kili (Dec 18 2021 at 00:44):

(with some new TODOs added for 2&3's output colors, redrawn text, loading bars, etc.)

view this post on Zulip jan kili (Dec 18 2021 at 01:17):

I'm new to FP, and I imagine there are multiple patterns that could be employed here to simplify the interface and support modularity

view this post on Zulip Joshua Warner (Dec 18 2021 at 02:25):

A few thoughts, in no particular order:

view this post on Zulip Joshua Warner (Dec 18 2021 at 02:27):

I'd highly recommend looking at the interface WASI provides (in the web-assembly world), as a model of a well-designed API for dealing with all these things.

view this post on Zulip Lucas Rosa (Dec 18 2021 at 02:34):

nice good points

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 02:55):

With file handles, I advise enforcing scope. So instead of openFile and requiring a closeFile later, have a withFile. That way leaks can't happen

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 02:55):

WithFile would take a lambda

view this post on Zulip jan kili (Dec 18 2021 at 03:15):

These are good points, I'll revise the interfaces and post a v2 :)

view this post on Zulip jan kili (Dec 18 2021 at 03:21):

@Brendan Hansknecht Like this? withFile : Str, (File -> Task {} []) -> Task {} [ FileNotFound, PermissionDenied ]

view this post on Zulip jan kili (Dec 18 2021 at 03:23):

@Joshua Warner If file I/O is improved with something like withFile, would stdin reading look like this? withStdin : (File -> Task {} []) -> Task {} [ BrokenPipe ]

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 03:24):

Something like that, though I guess the error would need to be passed into the lambda?

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:24):

I would expose the stdin file handle as an argument passed to main (probably part of a larger object)

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 03:24):

Maybe not though. Maybe the lambda should be selfed contained and the error is exposed elsewhere

view this post on Zulip jan kili (Dec 18 2021 at 03:25):

I would expect the lambda to only handle the nominal case, with error handling outside the withFile call

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 03:26):

Yeah. Just trying to figure out the exact error handling process if the file doesn't exist and you want to try a different one

view this post on Zulip jan kili (Dec 18 2021 at 03:26):

@Joshua Warner Interesting, since they don't really need to be opened or closed

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:27):

Exactly!

view this post on Zulip jan kili (Dec 18 2021 at 03:28):

@Joshua Warner Regarding the Request bikeshedding, my intent for that was to provide a low-level exposure of execution context - essentially what did the user request: "./path/to/my_executable --flag arg arg" ==> Request { executablePath: "./path/to/my_executable", rawArguments: "--flag arg arg" }

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:28):

Then you can tell if a given function reads/writes to the terminal just by looking at it's signature (i.e. does it take the handle to read/write to as an argument) - which seems like a useful property for making code auditable

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:28):

Re Request: Ahhh intersting; why not expose that as part of Args?

view this post on Zulip jan kili (Dec 18 2021 at 03:29):

I was thinking some developers may not want the fancy arg parsing

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:30):

Yep, makes sense. But from an 'api understandability' perspective, I think it belongs somewhere in the same module/namespace as the rest of the arg parsing

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:30):

I'd still highly recommend taking a look at WASI for inspiration: https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-overview.md

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:31):

That's a shining example of this sort of interface 'done right', IMO

view this post on Zulip jan kili (Dec 18 2021 at 03:31):

And since it's ambiguous where commands end and args begin... gh repo clone https://github.com/cli/cli

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:32):

Arg parsing is a _hard problem_ I wouldn't put too much weight on solving it at the same time as everything else.

view this post on Zulip jan kili (Dec 18 2021 at 03:34):

Regarding stream handles, should those look significantly different than file handles?

view this post on Zulip jan kili (Dec 18 2021 at 03:35):

Regarding bytes vs. strings, should file I/O support bytes as well?

view this post on Zulip jan kili (Dec 18 2021 at 03:35):

Regarding env vars, yes. :laughing:

view this post on Zulip jan kili (Dec 18 2021 at 03:36):

Regarding a project/platform name, any ideas?

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:43):

Regarding stream handles, should those look significantly different than file handles?

There's a pretty big design space for how to represent file handles in a type system, and what they can do. Here are some of the things that you can do (in posix) on a file handle:

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:44):

I'm of the opinion that actually encoding these in the type system somehow is good

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:45):

I'd also encourage "keeping it _reasonably_ simple" - i.e. don't actually try to support all of that

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:46):

Whether you can read/write/seek are all independent properties of a file handle

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:47):

Well, kinda. I guess a bunch of the combinations are not actually useful in practice.

view this post on Zulip jan kili (Dec 18 2021 at 03:47):

Wowee!

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:47):

A file handle that you can only seek but not read or write is not super useful.

view this post on Zulip jan kili (Dec 18 2021 at 03:48):

I can imagine certain executables utilizing those features

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:48):

Knowing the ins and outs of using file systems in creative ways has been a pretty critical part of my job for the last few years ;)

view this post on Zulip jan kili (Dec 18 2021 at 03:50):

Assuming a good typing system and clean interfaces, which would be best:

  1. Have a File interface that only supports simple file I/O
  2. Have a File interface that supports all of that
  3. Have two interfaces, maybe SimpleFile and ComplexFile

view this post on Zulip jan kili (Dec 18 2021 at 03:50):

  1. Start with #1, and put #2/#3 on the "maybe" roadmap

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:51):

I think Java has a pretty good design here. They have InputStreams, OutputStreams, and (IIRC) RandomAccessFiles

view this post on Zulip jan kili (Dec 18 2021 at 03:52):

(I'm so happy we have your domain knowledge here)

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:52):

RandomAccessFile could then be specialized into readable, writable, and readable+writable

view this post on Zulip jan kili (Dec 18 2021 at 03:52):

RAF is a file in RAM?

view this post on Zulip jan kili (Dec 18 2021 at 03:53):

Lost when the handle is lost?

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:53):

random access, as in "you can call seek()" on it

view this post on Zulip jan kili (Dec 18 2021 at 03:53):

Oh, nevermind

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:58):

An (IMO) very underappreciated thing you can do with handles - across basically all systems these days - is open a handle to a directory. With a directory handle you can do things like:

view this post on Zulip Joshua Warner (Dec 18 2021 at 03:59):

The really key thing about handles is that they "follow" moves - both for normal file handles and directory handles like the above.

view this post on Zulip jan kili (Dec 18 2021 at 04:01):

For a simple CLI, would this only apply for files that are moved after the execution starts but before it finishes?

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:01):

Yep. To be fair, it's not a commonly-used property. But it can save your bum sometimes.

view this post on Zulip jan kili (Dec 18 2021 at 04:02):

(Unrelated, is this thread taking up too much space in this chatroom? I don't know the etiquette here.)

view this post on Zulip jan kili (Dec 18 2021 at 04:02):

@Brendan Hansknecht Should we move this discussion elsewhere?

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 04:04):

This is a fine space for it. I think it would be super useful to make a summary doc/gist so that people can see the current plan

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 04:05):

That way as things change, we can update and discuss the paired doc

view this post on Zulip jan kili (Dec 18 2021 at 04:05):

I'm working on that now, also considering starting a repo for it (would a gist be better at first? I've never used gists)

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:05):

Here's one pretty general thing to consider. In terms of "ways apps access the filesystem", there's a bunch of different access patterns apps use. A web server will tend to use the FS in a very different pattern from a database server, which will be different still from things like a compiler, or a utility for working with .zip files.

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:06):

The posix interface has to serve _all_ of those needs - which in practice means it serves _none_ of them perfectly.

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 04:06):

Also, I think as a base goal, I think you may want to look at 3 versions of the api.
1) Super simple, just load the entire thing into string or bytes.
2) Streaming, basically can pipe bytes in for writing or pipe bytes out for reading
3) Random access file.

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 04:07):

I think 1 is the most important for simple things, but 2 become very important for representing more things and deal with large inputs.

view this post on Zulip Brendan Hansknecht (Dec 18 2021 at 04:07):

3 is very important in some cases, but for a lot of starter cli apps, it may not be needed at all.

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:10):

And, expanding on that above thought just a little bit more: With Roc, I think we have the opportunity to make several platforms that aren't under the pressure to be everything to everyone. A database will be written based on a different Platform than a compiler. And that's ok. In fact, it's _good_ since having a very specific target app in mind can make the interface you design much better.

view this post on Zulip jan kili (Dec 18 2021 at 04:11):

All true, which raises the question of what types of apps this platform is aiming to support?

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:12):

I'd encourage starting with a thing you want to write in Roc and then writing the platform that would be perfect for that thing. And then going from there.

view this post on Zulip jan kili (Dec 18 2021 at 04:12):

To that end, it would be a cobra/oclif competitor

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:14):

Oh, huh! TIL. Gonna have to look into that.

view this post on Zulip jan kili (Dec 18 2021 at 04:14):

Designed for packaging a set of Roc functions (aka commands) into a structured, terminal-friendly way (help, man, etc.)

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:15):

If those are pure functions, your job is super easy

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:15):

If you're trying to be the thing that everyone uses to write commands for the terminal, you've really got your work cut out for you.

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:16):

This is why I'd recommend starting from the _app_ that you want to support and working from there.

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:17):

Supporting unzip -p a/b/c.txt file.zip > c.txt will have very different requirements from... I dunno, math 1+2

view this post on Zulip jan kili (Dec 18 2021 at 04:19):

App #1: math 1+2

view this post on Zulip jan kili (Dec 18 2021 at 04:23):

One thing that's interesting here is that Roc's platform concept is so fundamentally different from other programming languages.

view this post on Zulip jan kili (Dec 18 2021 at 04:24):

For example, cobra is THE (clear best) way of writing CLIs in Golang. oclif is THE (clear best) way of writing CLIs in Node.js.

view this post on Zulip jan kili (Dec 18 2021 at 04:24):

However, neither of those libraries handle file I/O. That's delegated to your file I/O library of choice, since each library can interact with the underlying OS/platform independently.

view this post on Zulip jan kili (Dec 18 2021 at 04:26):

For a Roc platform to ergonomically provide the same level of abstractions as cobra or oclif do, would it also need to support every I/O & side effect your app could possibly need? It seems so, which is such a tall order.

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:26):

It really sounds to me like what you _actually_ want to build is a library for arg parsing

view this post on Zulip jan kili (Dec 18 2021 at 04:26):

Therefore, is a cobra competitor or oclif competitor impossible in the Roc ecosystem?

view this post on Zulip Joshua Warner (Dec 18 2021 at 04:26):

(not a platform)

view this post on Zulip jan kili (Dec 18 2021 at 04:31):

Maybe! Perhaps schema-based I/O parsing/formatting is truly independent of the underlying I/O system.

view this post on Zulip jan kili (Dec 18 2021 at 04:35):

However, I would still love to see+use a community-supported CLI-building platform that is optimized for real-world use (file I/O, error handling, etc.), since roc/examples/cli seems optimized more for simplicity/education (which it does very well).

view this post on Zulip jan kili (Dec 18 2021 at 07:29):

I've started prototyping it here: https://github.com/JanCVanB/Rocli :smile:

view this post on Zulip jan kili (Dec 25 2021 at 23:19):

Joshua Warner said:

It really sounds to me like what you _actually_ want to build is a library for arg parsing

Your words have been bouncing around my head this week :grinning_face_with_smiling_eyes: For now, I agree. I'm currently redesigning the library to be platform-agnostic, and maybe as a future step it will make sense to add a more-powerful CLI platform to go along with it. That might be where @Erwin Kuhn comes in!


Last updated: Jun 16 2026 at 16:19 UTC