roc-lang / basic-cli Goto Github PK
View Code? Open in Web Editor NEWA basic Command-Line Interface platform
License: Universal Permissive License v1.0
A basic Command-Line Interface platform
License: Universal Permissive License v1.0
Trying to access the Utc
module that appears to be part of the basic-cli
app "myapp"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3.1/97mY3sUwo433-pcnEQUlMhn-sWiIf_J9bPhcAFZoqY4.tar.br" }
imports [
pf.Utc,
...
$ roc dev
Downloading https://github.com/roc-lang/basic-cli/releases/download/0.3.1/97mY3sUwo433-pcnEQUlMhn-sWiIf_J9bPhcAFZoqY4.tar.br
into <user>/.cache/roc/packages
── FILE NOT FOUND ──────────────────────────────────────────────── UNKNOWN.roc ─
I am looking for this file, but it's not there:
<user>/.cache/roc/packages/github.com/roc-lang/basic-cli/releases/download/0.3.1/97mY3sUwo433-pcnEQUlMhn-sWiIf_J9bPhcAFZoqY4/Utc.roc
Is the file supposed to be there? Maybe there is a typo in the file
name?
When I inspect that directory, indeed there is no Utc.roc
file. It's not clear to me whether this is intended to be a public module, but the functions inside looked useful.
This should fix a crash when piping things to a basic-cli app, see zulip.
This would replace the current default which is showing the docs for the main branch. Copied from an idea in Zulip by @Anton-4.
Currently, we have a Task
for exiting the process immediately. This is also currently the only way to exit with a nonzero exit code, so if you want to translate errors into nonzero exit codes, you have to explicitly run that task.
There would be a couple of advantages to having main
be Task {} U32
:
U32
. You don't have to run a separate task.Task
types. For example, if a Task
has an error type of *
, you know it's not doing an early exit.The reason to have it be Task {} U32
instead of Task U32 []
is because:
main = Stdout.line "Hello, World!"
- you don't need to learn about error handling right away; that can come later.{}
(e.g. Stdout.line
, File.write
, etc.) into 0
A great way to make an impactful contribution is to read through the docs and try to improve them. Feel free to select me to review your PR.
I was going through the tutorial and got to creating an application https://www.roc-lang.org/tutorial#building-an-application. I copied and pasted the code and I get this error
An internal compiler expectation was broken.
This is definitely a compiler bug.
Please file an issue here: https://github.com/roc-lang/roc/issues/new/choose
thread 'main' panicked at 'No such file or directory (os error 2)', crates/linker/src/metadata.rs:58:71
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Here's my system information:
OS: Pop!_OS 22.04 LTS
Roc Version: roc_nightly-linux_x86_64-2022-11-27-468be47.tar.gz
Hi all, with the latest nightly built from source on Ubuntu 20 (WSL), running this example hangs the Rust compiler:
$ roc http-get.roc
thread 'main' panicked at 'assertion failed: !self.not_reference_counted.contains(symbol)', crates/compiler/mono/src/inc_dec.rs:98:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
^C
It does this for : packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.3.2/tE4xS_zLdmmxmHwHih9kHWQ7fsXtJr7W7h3425-eZFk.tar.br" }
as well as for this version: packages { pf: "../src/main.roc" }
Any idea what could be wrong here? Thanks!
duration = Utc.deltaAsNanos start finish |> Num.toStr
Stdout.line "Completed in \(duration)ns"
start and finish can be shown with dbg, and duration also when removing |> Num.toStr
Num.toStr probably doesn't work on U128 (which is the type of duration), so nothing (but also no error) is displayed.
We should have a compiler error for the situation where an exposed type includes a named type that's not exposed, but we don't yet. 😅
Right now we only have a devShell...
The lock file has been keeping basic-cli using a version of roc-std
from mid January. As such, it will break if passed a seamless slice.
I think it may be worth removing the lock file from basic-cli so that we always pull in the latest roc-std when publishing it. Otherwise, maybe we should add a cargo update
to the publishing process? Fundamentally we want this to stay in sync.
ubuntu linux, Dir.list is crashing with error
Dir.list...
munmap_chunk(): invalid pointer
roc nightly pre-release, build from commit 1aa928b on Sa 19 Nov 2022 09:32:56 UTC
Roc apps built with basic-cli can have a size in excess of 100MB, this should be investigated and reduced.
app "helloWorld"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br" }
imports [pf.Task.{ Task }, pf.Stdout]
provides [main] to pf
hello : Task {} *
hello =
Stdout.line "Hello Alice"
hi : Task {} *
hi =
Stdout.line "Hey Bob"
main =
_ <- Task.await hello
hi
This yields:
❯ ./target/release/roc check examples/helloWorld.roc
── TYPE MISMATCH ───────────────────────────────────── examples/helloWorld.roc ─
This 2nd argument to await has an unexpected type:
16│> _ <- Task.await hello
17│>
18│> hi
The argument is an anonymous function of type:
{} -> Task {} *
But await needs its 2nd argument to be:
{} -> Task {} *
Tip: Any connection between types must use a named type variable, not
a *!
────────────────────────────────────────────────────────────────────────────────
1 error and 0 warnings found in 51 ms.
I sort of get why this happens but I believe this should work, unlike #1931 we're literally restating the type of Stdout.line
.
Context: #2052
It is now part of the builtins and no longer needed in platforms.
I have this minimal reproducing example with a basic GET to a specific API:
app "http-network-error"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.7.0/bkGby8jb0tmZYsy2hg1E_B2QrCgcSTxdUlHtETwm5m4.tar.br" }
imports [pf.Http, pf.Task.{ Task }, pf.Stdout]
provides [main] to pf
main : Task {} I32
main =
request = {
method: Get,
headers: [],
url: "https://api-extern.systembolaget.se/site/V2/Search/Site",
body: Http.emptyBody,
timeout: NoTimeout,
}
output <- Http.send request
|> Task.onErr \err -> err
|> Http.errorToString
|> Task.ok
|> Task.await
Stdout.line output
On Apple silicon it works as expected (no credentials passed, hence 401):
~/S/p/roc ❯❯❯ uname -a
Darwin Richards-MacBook-Pro.local 23.0.0 Darwin Kernel Version 23.0.0: Fri Sep 15 14:43:05 PDT 2023; root:xnu-10002.1.13~1/RELEASE_ARM64_T6020 arm64
~/S/p/roc ❯❯❯ roc --version
roc nightly pre-release, built from commit e65f14fa496 on Sat Dec 2 09:15:51 UTC 2023
~/S/p/roc ❯❯❯ roc dev http-network-error.roc
Request failed with status 401
But on my Windows machine running WSL2 it returns NetworkError
:
~/s/b/examples ❯❯❯ uname -a
Linux DESKTOP-4Q7NKTK 5.15.133.1-microsoft-standard-WSL2 #1 SMP Thu Oct 5 21:02:42 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
~/s/b/examples ❯❯❯ roc --version
roc nightly pre-release, built from commit e65f14f on Sa 02 Dez 2023 09:07:00 UTC
~/s/b/examples ❯❯❯ roc dev http-network-error.roc
Network error
However, querying other API:s such as https://catfact.ninja/fact
work well on WSL2 with the same example code. Also, cloning this repo and importing the platform as packages { pf: "../src/main.roc" }
works with the troublesome API.
Any idea of why, or any suggestions on how to further debug?
There should be a Task.withArenaAlloc
function that initializes an arena allocator before running the task.
A while ago, I tried implementing it as an example: roc-lang/roc#4943
In this implementation, the function takes (U8 -> Task a err)
instead of Task a err
to prevent allocations from being performed in the task expression before the arena allocator is initialized. A cleaner and more reliable solution would be for the function's type to be a, (a -> Task b err) -> Task b err
.
There could also be a similar function of type a, (a -> b) -> b
for when no tasks need to be run.
At the moment, when an app using this platform is in raw mode and panics, the terminal will still be in raw mode.
It would be nicer if it automatically disabled raw mode on panics.
https://roc.zulipchat.com/#narrow/stream/304641-ideas/topic/Basic.20cli.2C.20disable.20raw.20mode.20on.20panic is the corresponding discussion on zulip.
@Hasnep has written https://github.com/Hasnep/bundle-roc-library which can bundle roc libraries.
Should we use this for basic-cli
?
Currently, we aren't correctly setting the exit code and it can be random.
In lib.rs
, we have pub extern "C" fn rust_main()
. It does not return an exit code.
Then in host.c
we have int main() { return (int)rust_main(); }
. We take the void return value and cast it to an int.
we should either change that to:
int main() {
rust_main();
return 0;
}
Or we should change rust_main
to return an int.
isDir : Path -> Bool
isFile: Path -> Bool
isSymLink: Path -> Bool
type: Path -> [IsFile, IsDir, IsSymLink]
I'm not sure if we should add an argument like [CleanWrite, Append]
to the write functions or create separate append functions. We should take a quick look how other languages do this.
OS: macOS 13.2.1
(I know there's a warning that this is not officially supported, so please feel free to ignore this whole issue if this is the root cause)
Roc Version: roc nightly pre-release, built from commit 512b936 on Sun Mar 12 09:08:15 UTC 2023
Note: I'm not sure if this is a platform error, or a roc-lang error.
I'm able to successfully run the http-get
example, code. However, when I change it to use the record update syntax, it fails to run with:
thread 'main' panicked at 'no entry found for key', crates/compiler/alias_analysis/src/lib.rs:1404:28
stack backtrace:
0: 0x101284637 - __mh_execute_header
1: 0x10066c70a - __mh_execute_header
2: 0x10127cddc - __mh_execute_header
3: 0x10128880b - __mh_execute_header
4: 0x10128854b - __mh_execute_header
5: 0x101288dbf - __mh_execute_header
6: 0x101288d04 - __mh_execute_header
7: 0x101287198 - __mh_execute_header
8: 0x101288a02 - __mh_execute_header
9: 0x10254da03 - __ZN4llvm15SmallVectorBaseIyE8grow_podEPvmm
10: 0x1006693fb - __mh_execute_header
11: 0x1006693ac - __mh_execute_header
12: 0x10254d899 - __ZN4llvm15SmallVectorBaseIyE8grow_podEPvmm
13: 0x1008b61ed - __mh_execute_header
14: 0x1008ab26d - __mh_execute_header
15: 0x1008a9fcd - __mh_execute_header
16: 0x100c27cb3 - __mh_execute_header
17: 0x10096a706 - __mh_execute_header
18: 0x100968cf0 - __mh_execute_header
19: 0x10096caaa - __mh_execute_header
20: 0x10096bf15 - __mh_execute_header
21: 0x100a3c82a - __mh_execute_header
22: 0x1008a463c - __mh_execute_header
23: 0x10089f0a6 - __mh_execute_header
24: 0x10089f0c5 - __mh_execute_header
25: 0x101276d5c - __mh_execute_header
26: 0x1008a5caf - __mh_execute_header
27: 0x7ff816a4d310 - <unknown>
Here's the code:
app "hello"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.2.0/8tCohJeXMBUnjo_zdMq0jSaqdYoCWJkWazBd4wa8cQU.tar.br" }
imports [pf.Stdout, pf.Http.{defaultRequest}, pf.Task]
provides [main] to pf
main =
request = { defaultRequest & url: "http://aaronstrick.com" }
output <- Http.send request
|> Task.onFail (\err -> err |> Http.errorToString |> Task.succeed)
|> Task.await
Stdout.line output
Note that request = defaultRequest
runs fine (providing the InvalidRequest
error I would expect.
When trying to compile this there were some minor changes to make:
I tried to fork and do a pull request, but today the whole business with SSH keys needed for that didn't want to work for me.
So here is the update code:
app "test"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.4.0/DI4lqn7LIZs8ZrCDUgLK-tHHpQmxGF1ZrlevRKq5LXk.tar.br" }
imports [
pf.Stdout, pf.Task.{ Task },
]
provides [main] to pf
main =
myrecord : Task { apples : List Str, oranges : List Str } U32
myrecord = Task.succeed {
apples: <- getFruit Apples |> Task.batch,
oranges: <- getFruit Oranges |> Task.batch,
}
{ apples, oranges } <- myrecord |> Task.await
"Apples: "
|> Str.concat (Str.joinWith apples ", ")
|> Str.concat "\n"
|> Str.concat "Oranges: "
|> Str.concat (Str.joinWith oranges ", ")
|> Stdout.line
|> Task.mapFail \_ -> 1
getFruit : [Apples, Oranges] -> Task (List Str) *
getFruit = \request ->
when request is
Apples -> Task.succeed ["Granny Smith", "Pink Lady", "Golden Delicious"]
Oranges -> Task.succeed ["Navel", "Blood Orange", "Clementine"]
# =>
# Apples: Granny Smith, Pink Lady, Golden Delicious
# Oranges: Navel, Blood Orange, Clementine
This script should replace every ../src/main.roc
in the examples with a link to the latest platform release and run all_tests.sh
The idea here is to make main
more beginner-friendly without noticeably affecting things experienced users.
If main
has the type Task {} *
(which it achieves by translating any unhandled Task
errors into crashes automatically), then beginners don't need to learn about how to gracefully handle errors right away. They can pretty much just ignore errors and get a crash if something goes wrong, allowing them to focus on learning other things - e.g. how to chain tasks together.
Advanced users can continue to annotate their main
as main : Task {} []
(which of course is compatible with main : Task {} *
) and then they'll still get exhaustiveness errors for any task errors they forgot to handle, just like today!
app "example"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.1.2/3bKbbmgtIfOyC6FviJ9o8F8xqKutmXgjCJx3bMfVTSo.tar.br" }
imports [
pf.Stdout,
pf.Task.{ Task },
pf.File,
pf.Path.{ Path },
]
provides [ main ] to pf
main : Task {} []
main =
task =
_ <- File.readUtf8 (Path.fromStr "i-dont-exist.txt") |> Task.await
Stdout.line ""
Task.onFail task \_ -> crash "ooops"
luke@192-168-1-108 aoc-2022 % roc dev test.roc
thread '<unnamed>' panicked at 'not yet implemented: Report a file open error', src/lib.rs:421:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5
There can be useful data behind a NetworkError, for example:
hyper::Error(
Connect,
ConnectError(
"dns error",
Custom {
kind: Uncategorized,
error: "failed to lookup address information: Name does not resolve",
},
),
),
We should not discard that kind of data.
To start of simple, we can change NetworkError
to NetworkError Str
.
This is a very common pattern, we should probably provide a single function that does this
{} <- Stderr.line "Error: calculation failed" |> Task.await
Task.err 1
❯ ./target/release/roc examples/helloWorld.roc
thread 'main' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime', src/lib.rs:678:42
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5
app "helloWorld"
packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.7.0/bkGby8jb0tmZYsy2hg1E_B2QrCgcSTxdUlHtETwm5m4.tar.br" }
imports [pf.Http, pf.Task.{ Task }, pf.Stdout]
provides [main] to pf
main : Task {} I32
main =
request = {
method: Get,
headers: [],
url: "http://www.example.com",
body: Http.emptyBody,
timeout: TimeoutMilliseconds 5000,
}
_ <- Http.send request
|> Task.attempt
Stdout.line "Done"
timeout: NoTimeout
does work.
This error should also sate the user specified timeout.
This should be of similar quality to https://github.com/roc-lang/roc/blob/main/CONTRIBUTING.md
+ ./roc_nightly/roc build --linker=legacy ./examples/args.roc
🔨 Rebuilding platform...
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x1b\x00\x00\x00\x12\x00\x00\x00\xbe\xd12$b\x077\x15"), definition of value binding ValueId(59): could not find func in module ModName("UserApp") with name FuncName("\x8c\x00\x00\x00\x12\x00\x00\x00\x1d\xca$\xf15\xe8\xd8\x98")', crates/compiler/gen_llvm/src/llvm/build.rs:5094:19
This is a recent regression in roc, I should be able to bisect this soon.
This is because in host.c we use the return value of rust_main()
as the exit code, but rust_main
doesn't return anything right now.
To fix this:
rust_main
to return i32
roc_main
's task returns Ok {}
then return 0
roc_main
's task returns an Err
then return the 32-bit integer inside it$ ./target/release/roc run examples/args.roc --linker=legacy -- log -b 3 --num 81
args-example
A calculator example of the CLI platform argument parser
COMMANDS:
div
OPTIONS:
--dividend, -n the number to divide; corresponds to a numerator (integer, 64-bit signed)
--divisor, -d the number to divide by; corresponds to a denominator (integer, 64-bit signed)
log
OPTIONS:
--base, -b base of the logarithm (integer, 64-bit signed)
--num the number to take the logarithm of (integer, 64-bit signed)
The program name "args-example" was not provided as a first argument!
The weird thing is, this only happens when using a basic-cli release, not when building the platform from source.
We use the rust args_os
function under the hood, in the docs it is mentioned that "The first element is traditionally the path of the executable, but it can be set to arbitrary text, and might not even exist. This means this property should not be relied upon for security purposes."
$ roc build ./examples/hello-world.roc
[ ... ]
$ ./examples/hello-world
Hello, World!
Segmentation fault (core dumped)
See #122 to reproduce.
Tasks are pretty complicated, each function should have an excellent explanation.
We currently have writeErrToStr
and readErrToStr
, but these just convert the tag name to a Str
, they do not provide a high quility error message.
FileWriteErrToErrMsg
and FileReadErrToErrMsg
should convert each variant of a WriteErr or ReadErr to a high quality error message, for example with WasADirectory
:
File write error:
WasADirectory:
I tried to write to:
<path>
but it was a directory, I can only write to a file.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.