Happy to make an issue for this, but I just want a sanity check - does this make sense to anyone?
Module A:
main =
_ <- await (line "")
double = \x -> x * 2
doubleBind = \x -> (\_ -> double x)
six = (doubleBind 3) {}
line (Num.toStr six)
Output A:
6
Module B:
main =
_ <- await (line "")
double = \x -> x * 2
doubleBind = \x -> (\_ -> double x)
doubleThree = doubleBind 3
six = doubleThree {}
line (Num.toStr six)
Output B:
thread 'main' panicked at 'internal error: entered unreachable code: symbol/layout `8.IdentId(8)` ProcLayout { arguments: [Struct([]), LambdaSet(LambdaSet { set: [(`8.IdentId(8)`, [Builtin(Int(I64))])], representation: Struct([Builtin(Int(I64))]) })], result: Builtin(Int(I64)) } combo must be in DeclarationToIndex', compiler/mono/src/borrow.rs:227:9
Changed lines:
six = (doubleBind 3) {}
vs.
doubleThree = doubleBind 3
six = doubleThree {}
(I've removed roc/examples/cli/platform
boilerplate for succinctness)
Is there an obvious reason that defining doubleThree
causes a panic? roc check
is clean on both, btw.
That looks like a compiler bug. The panic is in a file related to refcount insertion. That phase comes after type checking, so wouldn't show up in roc check.
Thanks! I'll report it and temporarily modify my code to use an anonymous binding/closure :)
Great!
That's a relief - I was worried that I'd messed up some great-grandchild binding in my confusing code... but by the time I boiled it down to the minimal reproduction above I was half-sure that something fishy was going on.
https://github.com/rtfeldman/roc/issues/2322
HI all, I am trying to set up my m1 mac for development with nix (using rosetta. i.e. platform is still x86_64). I am getting the following error
CMake Error at /nix/store/gxqi9sjvvwmf86qx075alg376x32s0zc-llvm-7.1.0-dev/lib/cmake/llvm/TableGen.cmake:14 (message):
LLVM_TABLEGEN_EXE not set
Call Stack (most recent call first):
include/clang/Driver/CMakeLists.txt:2 (tablegen)
Also tried with UTM and nix-shell also fails on aarch64 (different error), next week I will try another VM on x86 which would be slower but perhaps easier to set up.
I wonder if anyone has experience to share on M1 macs with nix (macos) or nixos.
I use an m1 mac with nix. No rosetta
Tends to just work
Though has some limitations were we don't fully support arm. Not a problem for the main llvm backend.
something must be off on my setup, this is what I get with system = aarch64-darwin -> " >...libredirect.dylib' (mach-o file, but is an incompatible architecture (have 'arm64', need 'arm64e'))"
I have never seen that, when does it come up?
Also, my setup may be leaky (i.e, I also have rust outside of nix and maybe zig), which may fix potential nix issues
found it was OpenAL (audio fw specific to macos), not sure why yet but commenting it out allows me to run a shell
doing that on x86 system, but that arm64 thing is from when I was trying aarch64-darwin
I use the M1 Max and no nix
How can I write a function that accepts a tag argument that could be either an A
tag or a B
tag? I tried this, but it doesn't work:
» a : [ A Str ]
… a = A "A"
… f : [ A Str, B Str ] -> Str
… f = \x ->
… when x is
… A y -> y
… B y -> y
… f a
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
The 1st argument to f is not what I expect:
11│ f a
^
This a value is a:
[ A Str ]
But f needs the 1st argument to be:
[ A Str, B Str ]
Tip: Looks like a closed tag union does not have the B tag.
Tip: Closed tag unions can't grow, because that might change the size
in memory. Can you use an open tag union?
Yeah I've run into this kind of thing as well. It's because of the language supporting open tag unions. So in theory A Str
could be part of any other union besides this one. Longer term I imagine we'll be able to figure it out, I'm not sure. But we don't right now.
I have found a type annotation works in this kind of situation. If you give a name to your union like AorB : [A Str, B Str]
and then give a
a type annotation like a : AorB
Or I suppose you could probably type it out again rather than naming it! But you'll probably want to name it anyway.
Interesting, so now I notice that the inferred type for a = A "A"
is [ A Str ]*
This is confusing to me, because how could a
ever accumulate additional tags in its union? It will only ever be that A
tag.
Yeah I agree and I suspect that it's more of a bug than a feature. But I don't work on that part of the compiler so I'm not 100% sure!
Is the real meaning more like "a
can be used anywhere a tag union matches this pattern: [ A Str ]*
"?
I was initially interpreting its inferred type as saying "a
is a tag could be A Str
or something else"
That's what I gathered from this part of the tutorial:
If you have a closed union, that means it has all the tags it ever will, and can't accumulate more.
If you have an open union, that means it can accumulate more tags through conditional branches.
Hold on, that's not the type of a
! There's no *
In your example code
True, but in the REPL if I drop my manual annotation, I get this:
The rockin’ roc repl
────────────────────────
Enter an expression, or :help, or :exit/:q.
» a = A "A"
… a
A "A" : [ A Str ]*
»
So it's a closed union that says a
can only be A Str
and never any other tag. So that's a different type.
The "inferred" type
yes but your two examples have different types for the same expression
The inferred type will have a *
but if you annotate it without one you are telling the compiler you explicitly do not want any other tag to be a valid part of the type of a
.
I see, so basically if a function can handle multiple tags, it's illegal to call it with an input that can't be all of those tags.
i.e. if I have an HTTP response that is somehow guaranteed to be a 404
, I can't pass that to a function that processes both 200
and 404
responses?
That very much depends on the types they have!
There are lots of ways to represent HTTP responses in the type system
Do you still get this error in the repl case with the inferred type?
No, I actually get a panic :laughing:
But that's probably irrelevant https://github.com/rtfeldman/roc/issues/2344
:laughing:
Could you please give me an example of a situation where I would want to manually annotate a def as being a closed tag union instead of an open one?
a : [ A Str ]
a = A "A"
vs.
a : [ A Str ]*
a = A "A"
The bottom one seems objectively better, if the top one can't be passed to functions that accept other tags.
I ask because I'm still confused what the distinction is. I get why functions would accept closed vs. open, but I don't get how a def can BE closed or open.
I don't know, I didn't design this feature, I was just trying to help with your specific error but you probably want to talk to someone who was involved in designing it
:) Makes sense, thanks!
I'll open a new topic for this, since you helped me figure out the silly thing.
I modified the example CLI platform to use Rust's print
instead of println
, and now I'm successfully writing a lot of Rust-based print
s to stdout
(with a \r
between each, so that the line overwrites itself)! However, after ~47,000 consecutive (very fast) writes to stdout, I'm getting a Segmentation fault: 11
. Since these writes are generated by recursive _ <- await ...
calls, is this an expected "stack overflow"-type problem with having too many callbacks? Is there a better way to do a lot of I/O tasks without causing such a segfault? My instinct is to replace the recursion with a while
loop, but that doesn't exist in Roc.
The only way I can think to get more writes out of this program would be to wrap the Roc main
function in a Rust while
loop at the platform level (and make the Roc main
function responsible for only one write), but I don't know enough about platforms to know if that would somehow break the platform/application contract.
The context for this I/O use case is that I'm experimenting with how one might use Roc to do ASCII animation for things like CLI loading spinners or simple CLI games (Snake? Space Invaders?) :smile:
I can help with one piece of this: Getting the compiler to generate while
loops.
Any function that is tail-recursive will be automatically transformed into a while
loop. If you're not familiar with that concept, here's an article that explains it: https://www.geeksforgeeks.org/tail-recursion/. Normally you need to refactor your code so that any value that's preserved from one iteration to the next becomes an argument of the recursive call. Those extra arguments usually make the function ugly to use from the outside world, so you end up wrapping it in another function to make it nicer.
Ah, that's super useful! Does that only apply to functions provide
d to the platform, or all of them?
@Brian Carroll Would this module get the while
loop optimization? It seems like it might, as it is still running and hasn't crashed yet :smiley:
#!/usr/bin/env roc
app "example"
packages { pf: "./roc/examples/cli/platform" }
imports [ pf.Stdout.{ line }, pf.Task.{ await } ]
provides [ main ] to pf
main =
_ <- await (line "")
tick 1000000000000
tick = \count ->
if count == 0 then
line "Done!"
else
# _ <- await (line (Num.toStr count))
tick (count - 1)
Nope, it's any function that calls itself and immediately returns the result. This is an optimisation that is implemented in pretty much every functional language. It's the standard trick for doing loops without mutation. I think it was figured out in the 70s, like everything else! It's called tail-call optimisation or tail-call elimination.
I had lots of fun learning to use this when I discovered it!
eh, in the above case tick
will be TCO'd
Would uncommenting the await
change that?
today, probably yes
but LLVM may be able to turn the recursion back into a loop after some inlining
I see! Thanks. So then... is it impossible today to avoid a stack overflow when infinitely-repeating an I/O cycle?
(I'm glad there's a path to clean TCO for I/O, though!)
This is actually an interesting question. So say we start with something of this form
tick = \count ->
if count == 0 then
Effect.putLine "done"
else
Effect.await (Effect.putLine "step") \{} -> tick (count - 1)
Where Effect a : [ @Effect (World -> { a, world: World } ) ]
and
Effect.await : Effect a, (a -> Effect b) -> Effect b
Effect.await = \@Effect thunk, toEffect ->
@Effect \world1 ->
{ a, world: world2 } = thunk world1
@Effect thunk2 = toEffect a
thunk2 world2
Inlining gives
tick = \count ->
if count == 0 then
Effect.putLine "done"
else
@Effect thunk = Effect.putLine "step"
toEffect = \{} -> tick (count - 1)
@Effect \world1 ->
{ a, world: world2 } = thunk world1
@Effect thunk2 = toEffect a
thunk2 world2
The @Effect
wrapper melts away
tick = \count ->
if count == 0 then
Effect.putLine "done"
else
thunk = Effect.putLine "step"
toEffect = \{} -> tick (count - 1)
\world1 ->
{ a, world: world2 } = thunk world1
thunk2 = toEffect a
thunk2 world2
We can clean this up slightly
tick = \count ->
if count == 0 then
Effect.putLine "done"
else
thunk = Effect.putLine "step"
toEffect = \{} -> tick (count - 1)
\world1 ->
{ a, world: world2 } = thunk world1
(toEffect a) world2
In this case we can inline toEffect
, because the data dependency on world2
ensures correct evaluation order
Also we can move thunk into the closure and inline it (this does not happen today, but it's semantically valid)
tick : I64 -> (World -> { a: {}, world : World })
tick = \count ->
if count == 0 then
Effect.putLine "done"
else
\world1 ->
{ a: _, world: world2 } = (Effect.putLine "step") world1
(tick (count - 1)) world2
now we lift functions to the toplevel, and run into issues with closures
helper \world1, { count } ->
{ a: _, world: world2 } = (Effect.putLine "step") world1
(tick (count - 1)) world2
tick : I64 -> (World -> { a: {}, world : World })
tick = \count ->
if count == 0 then
Effect.putLine "done"
else
helper ??? { count }
we need to complete the closure conversion
helper : World, { count : I64 } -> { a: {}, world : World }
helper \world1, { count } ->
{ a: _, world: world2 } = (Effect.putLine "step") world1
when tick (count - 1) is
C1 { msg } -> (Effect.putLine msg) world2
C2 { count } -> helper world2 { count }
tick : I64 -> [ C1 Effect.putLine { msg: Str }, C2 helper { count: I64 } ]
tick = \count ->
if count == 0 then
C1 { msg: "done" }
else
C2 { count }
and now helper
can be TCO'd.
given that we can do this with some simple rewriting, it seems plausible that LLVM could already figure this out today
indeed, for this example the LLVM gets there
%call1.i.i = tail call fastcc %str.RocStr @"#UserApp_tick_6cc39a125db7a4f2ba7ee17ffefa9ff1fe97baed9c849631614587aef96caa15"(i64 %operation_result.i.i.i)
ret %str.RocStr %call1.i.i
🤯
:bow:
How does tick
not need to call helper
?
How is its annotation (tick : I64 -> [ C1 Effect.putLine { msg: Str }, C2 helper { count: I64 } ]
) legal if Effect.putLine
and helper
are functions?
Are the functions called implicitly or something?
trying to figure out how to explain this, but that's kind of the idea yes
We're missing main here
main : Effect {}
main = tick 5
Based on the return type for tick
, this should be
main : [ C1 Effect.putLine { msg: Str }, C2 helper { count: I64 } ]
main = tick 5
This is the main function we expose. What it returns is really a suspended computation, or a recipe for a computation. We also expose an evaluator for this recipe. Intuitively
cheatyActualMain : Effect a -> a
cheatyActualMain = \effect ->
@Effect thunk = effect
thunk world
The closure transform turns that into (we're pretending here that cheatyActualMain main
is called:
cheatyActualMain : [ C1 Effect.putLine { msg: Str }, C2 helper { count: I64 } ] -> {}
cheatyActualMain = \closure ->
when closure is
C1 { msg } -> (Effect.putLine msg) world
C2 { count } -> helper world { count }
Where is World
defined? (I've never heard of "worlds" in Roc before.)
it's something I stole from haskell real quick to make sure the ordering of operations is fixed
you could say World : {}
haskell's IO monad is defined in this way
How does Effect a
unify with [ C1 Effect.putLine { msg: Str }, C2 helper { count: I64 } ]
? (I may be using the world "unify" incorrectly here, but I don't see how cheatyActualMain
can accept main
as its argument.
where IO a : (RealWorld -> (a, RealWorld)
right, so they don't unify but they are related in this case
in e.g. elm we think of function types as just a -> b
but, we extend that with an extra piece of information: what values (and with which types) are captured by the function
So given
identity = \x -> x
where elm would say
identity : a -> a
we under the hood say
identity : a [ C identity {} ] -> a
Whoa! So is [ C1 Effect.putLine { msg: Str }, C2 helper { count: I64 } ]
an annotation for only under-the-hood or is it also legal above-the-hood?
and more interestingly
f = \x -> Str.concat x y
(note, y is not bound within f, it is a free variable, and captured by this closure)
f : Str [ C f { y : Str } ] -> Str
the closure part is under-the-hood entirely
I'm wondering whether I can stitch these code snippets into a module and run it on ./roc/examples/cli/platform
in any case, to bring this together, we eliminate function values, and replace them by their captured environment. So main : Effect a
really has a function type main : World [ some things ] -> { a, World }
, and that function value we represent by the closure tag union
and then by doing some bookkeeping we can later know how to evaluate a closure value: simply match on all possible functions that it could be and pass along the arguments
Are these tricks possible in a simple application outside of its platform? Above the platform/application boundary?
For example, using the examples/cli/platform
as-is?
not in user code, because Effect
is made with a private tag
so you cannot unpack it or manually make an Effect
from a function
Ah, I see. Does that mean that today there's no platform-agnostic way to avoid a stack overflow when infinitely-repeating an I/O cycle?
there is no guaranteed way
Platforms have to explicitly support any infinite loops required for servers / games / long-running processes?
but with --optimize
tail optimization already happens
in these cases
I'd like to add Effect.forever : Effect a -> Effect b
for that
Thank you for all this info!
I'm disappointed that my pure-Roc CLI-based ASCII game hopes must be put on hold, but I'm hopeful it'll get support in the future :)
did you run into stack issues?
Yeah, I got a segfault after 47k frames
ah, ok
you did use --optimize
?
I didn't! What command is that a flag for?
you can do cargo run -- --optimize foo/bar/Baz.roc
Ooh, I'll try
And that might work with a mid-tick
_ <- await
?
certainly worth a try
nice!
so yeah we should figure out how to also make this work in non-optimized modes
Am I doing something silly?
Yes, I'm running unoptimized code!
Space Invaders, here I come.
Thank you!
(Side note, my mac went to sleep twice during the one minute I was running that infinite sleepless loop :joy:)
(I think I might need to add a sleep
function to the platform's API)
really, it can go to sleep during busy-waiting loops?
Thank you both for this really interesting discussion :smile:
Yeah, interesting stuff!
Great we can do this as well. Having a main loop with I/O inside it will be useful in all sorts of places!
I wonder if https://github.com/rtfeldman/roc/issues/1778 would still be reproducible with --optimize
:thinking:
I think the inliner is not able to make the same thing happen there. We definitely tried it with --optimize
define internal fastcc {} @Effect_forever_inner_fb5e512425fc9449316ec95969ebe71e2d576dbab833d61e2a5b9330fd70ee2({} %"5", { { %str.RocStr } } %"#arg_closure") {
entry:
br label %tailrecurse
tailrecurse: ; preds = %tailrecurse, %entry
%"5.tr" = phi {} [ %"5", %entry ], [ zeroinitializer, %tailrecurse ]
%"#arg_closure.tr" = phi { { %str.RocStr } } [ %"#arg_closure", %entry ], [ %call1, %tailrecurse ]
%struct_field_access_record_0 = extractvalue { { %str.RocStr } } %"#arg_closure.tr", 0
tail call fastcc void @"#Attr_#inc_2"({ %str.RocStr } %struct_field_access_record_0, i64 1)
tail call fastcc void @"#Attr_#dec_3"({ { %str.RocStr } } %"#arg_closure.tr")
tail call fastcc void @"#Attr_#inc_2"({ %str.RocStr } %struct_field_access_record_0, i64 1)
%call = tail call fastcc {} @Effect_effect_closure_putLine_9d9f29527a6be626a8f5985b26e19b237b44872b03631811df4416fc1713178({} zeroinitializer, { %str.RocStr } %struct_field_access_record_0)
%call1 = tail call fastcc { { %str.RocStr } } @Effect_forever_67abdd721024f0ff4eb3f4c2fc13bc5bad42db7851d456d88d203d15aaa450({ %str.RocStr } %struct_field_access_record_0)
br label %tailrecurse
}
it's beautiful
This is an Effect.forever
(used as e.g. Effect.forever (Effect.putLine "hello world")
to print that line forever) and llvm turns it into a loop
still needs some cleanup, and then maybe some variations that could iterate while some condition is true
Nice! Is this for use in appcode or platformcode?
a platform author could now expose Task.forever
building upon this function
so, both kinda
That's super useful.
wow, fantastic!!! :heart_eyes:
this may end up becoming one of the most commonly used functions among all Roc programs!
omg! I wanted this for an event loop when I was trying to wrap SDL in a platform
this is very cool!
This now also works
main : Task.Task {} []
main =
Task.loop 0 looper
looper = \n ->
if n < 10 then
s = Num.toStr n
{} <- Task.after (Task.putLine "Iteration: \(s)")
Task.succeed (Step (n + 1))
else
Task.succeed (Done {})
plus we get
forever : Task val err -> Task * err
forever = \task ->
helper = \{} ->
task
|> Effect.map \res ->
when res is
Ok _ -> Step {}
Err e -> Done (Err e)
Effect.loop {} helper
i.e. a forever
on tasks that terminates when a task fails
Yesss
Are you planning to add any of these examples to examples/
?
This feature is invaluable to web servers, games, etc! Thank you :D
the loop
and forever
task functions are added to the benchmarks and cli platforms in my PRs
no usage examples yet though
I might add some usage examples (unless you want to)
to give examples/cli/Echo.roc
some friends
this does make building a REPL in roc possible now
well I should say easier*
Should this cause an Eldritch horror to be unleashed on my REPL? :scared:
x : F32
x = 1.5
x * Num.toFloat 4
I'm thinking no: https://github.com/rtfeldman/roc/issues/2476 :laughing:
I'm back with more tag union panics! :laughing:
» x : [ Y U8 ]*
… x = Y 3
… x
thread '<unnamed>' panicked at 'invalid content in tag union variable: (114, RigidVar(SubsIndex<roc_module::ident::Lowercase>(0)))', compiler/mono/src/layout.rs:2002:23
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
^C
Am I doing something silly?
(a closed tag union works fine here)
» x : [ Y U8 ]
… x = Y 3
… x
Y 3 : [ Y U8 ]
I've got a second one! The silliness continues?
» wrap = \value -> Tag value
… wrap
<function> : a -> [ Tag a ]*
» wrap = \value -> Tag value
… wrapIt = wrap
… wrapIt
<function> : a -> [ Tag a ]*
» wrap = \value -> Tag value
… wrap 42
Tag 42 : [ Tag (Num *) ]*
» wrap = \value -> Tag value
… wrapIt = wrap
… wrapIt 42
thread 'main' panicked at 'Roc failed with message: "UnresolvedTypeVar compiler/mono/src/ir.rs line 6842"', repl_cli/src/lib.rs:137:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Could you file bugs for these? The second one is known I think but would be good to have a record of both!
Silliness detected? https://github.com/rtfeldman/roc/issues/3377
(I'm causing a segfault by substituting [0, 1, 2]
with List.range 0 3
)
I've encountered a second problem! Am I missing something about this syntax, because this seems like a reasonable recursion pattern to me:
» a = \b ->
… c = \d ->
… if d >= 10 then d else c (d+b)
… c 0
… a 6
thread 'main' panicked at 'internal error: entered unreachable code: symbol/layout `#UserApp.c` ProcLayout { arguments: [Builtin(Int(I64))], result: Builtin(Int(I64)) } combo must be in DeclarationToIndex', crates/compiler/mono/src/borrow.rs:165:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[jan@framey roc]$
(btw, that inner function is fine by itself)
» c = \d ->
… if d >= 10 then d else c (d+3)
… c 0
12 : Num *
»
I'll go ahead and report that second "internal error" problem as an issue, too.
I expect that I'm exhibiting minimal silliness here today. Sorry for being boring!
https://github.com/rtfeldman/roc/issues/3378
Ummm what am I missing here?
main =
deck = myDeck
{card} = dealMe deck
rank =
when card.rank is
Ace -> "Ace"
King -> "King"
Queen -> "Queen"
Jack -> "Jack"
suit =
when card.suit is
Club -> "Clubs"
Diamond -> "Diamonds"
Heart -> "Hearts"
Spade -> "Spades"
"My card is the \(card.rank) of \(card.suit)!\n"
── MISSING EXPRESSION ─ ..._roc/domain-modeling-in-roc/step_2__card_game_A.roc ─
I am partway through parsing a definition, but I got stuck here:
4│ provides [main] to pf
...
23│ main =
24│ deck = myDeck
25│ {card} = dealMe deck
26│ rank =
27│ when card.rank is
^
I was expecting to see an expression like 42 or "hello".
:thinking:
Whitespace bug? That is definitely what it looks like to me.
Ooh, I'll check for that
Might be worth running roc format and seeing the diff? might fix it?
Success!
I didn't try the formatter, but I think it was a funky newline.
Thank you :)
If it seems worth tracking/filing an issue, maybe we can make a better error message or fix the parsing around this.
While I have you here, any tips for this?
[jan@framey roc]$ export RUST_BACKTRACE=full
[jan@framey roc]$ cargo run ../domain-modeling-in-roc/step_2__card_game_A.roc
Finished dev [unoptimized + debuginfo] target(s) in 0.44s
Running `target/debug/roc ../domain-modeling-in-roc/step_2__card_game_A.roc`
thread '<unnamed>' panicked at 'not yet implemented', crates/compiler/mono/src/ir.rs:2382:23
stack backtrace:
0: 0x5635ba31fb9d - std::backtrace_rs::backtrace::libunwind::trace::hee598835bc88d35b
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
1: 0x5635ba31fb9d - std::backtrace_rs::backtrace::trace_unsynchronized::h9cdc730ba5cf5d72
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
2: 0x5635ba31fb9d - std::sys_common::backtrace::_print_fmt::h75aeaf7ed30e43fa
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/sys_common/backtrace.rs:66:5
3: 0x5635ba31fb9d - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h606862f787600875
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/sys_common/backtrace.rs:45:22
4: 0x5635ba34aadc - core::fmt::write::he803f0f418caf762
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/core/src/fmt/mod.rs:1190:17
5: 0x5635ba319d28 - std::io::Write::write_fmt::h70bc45872f37e7bb
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/io/mod.rs:1657:15
6: 0x5635ba321dd7 - std::sys_common::backtrace::_print::h64d038cf8ac3e13e
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/sys_common/backtrace.rs:48:5
7: 0x5635ba321dd7 - std::sys_common::backtrace::print::h359300b4a7fccf65
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/sys_common/backtrace.rs:35:9
8: 0x5635ba321dd7 - std::panicking::default_hook::{{closure}}::hf51be35e2f510149
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/panicking.rs:295:22
9: 0x5635ba321aa0 - std::panicking::default_hook::h03ca0f22e1d2d25e
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/panicking.rs:314:9
10: 0x5635ba322529 - std::panicking::rust_panic_with_hook::h3b7380e99b825b63
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/panicking.rs:698:17
11: 0x5635ba3221d9 - std::panicking::begin_panic_handler::{{closure}}::h8e849d0710154ce0
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/panicking.rs:586:13
12: 0x5635ba320064 - std::sys_common::backtrace::__rust_end_short_backtrace::hedcdaddbd4c46cc5
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/sys_common/backtrace.rs:138:18
13: 0x5635ba321f29 - rust_begin_unwind
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/panicking.rs:584:5
14: 0x5635b5b4ae83 - core::panicking::panic_fmt::he1bbc7336d49a357
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/core/src/panicking.rs:143:14
15: 0x5635b5b4ad4d - core::panicking::panic::h4241c5ccea17faca
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/core/src/panicking.rs:48:5
16: 0x5635b99cc490 - roc_mono::ir::from_can_let::he631a1fd2e7f1db7
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:2382:23
17: 0x5635b9a09970 - roc_mono::ir::from_can::h6c53f19073fb59f4
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:5932:33
18: 0x5635b996e417 - roc_mono::ir::from_can_let::{{closure}}::hb169aff3d0732e09
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:2206:25
19: 0x5635b99769d7 - roc_mono::ir::handle_variable_aliasing::h457631afa9a49d05
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:7025:16
20: 0x5635b99d0e75 - roc_mono::ir::from_can_let::he631a1fd2e7f1db7
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:2209:24
21: 0x5635b9a09970 - roc_mono::ir::from_can::h6c53f19073fb59f4
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:5932:33
22: 0x5635b99d6413 - roc_mono::ir::specialize_external::h014c8ba65786d364
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:2988:32
23: 0x5635b996ebf8 - roc_mono::ir::specialize_variable_help::hc0e86a21717b3946
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:3465:23
24: 0x5635b99dbbf3 - roc_mono::ir::specialize_variable::h0634205863a1b1ec
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:3414:5
25: 0x5635b99d4cb4 - roc_mono::ir::specialize_external_help::he85ecfa478515205
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:2767:33
26: 0x5635b99d4aa2 - roc_mono::ir::specialize_external_specializations::h03afe44495a518a6
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:2740:13
27: 0x5635b99d400e - roc_mono::ir::specialize_all::h38efe702f28bba33
at /home/jan/_code/_roc/roc/crates/compiler/mono/src/ir.rs:2673:9
28: 0x5635b7436e4e - roc_load_internal::file::make_specializations::h624990574ec76565
at /home/jan/_code/_roc/roc/crates/compiler/load_internal/src/file.rs:4519:13
29: 0x5635b743c47b - roc_load_internal::file::run_task::h6036f07fd619b017
at /home/jan/_code/_roc/roc/crates/compiler/load_internal/src/file.rs:4996:17
30: 0x5635b741559a - roc_load_internal::file::worker_task::hebe41c651db0920e
at /home/jan/_code/_roc/roc/crates/compiler/load_internal/src/file.rs:1817:34
31: 0x5635b7440e1a - roc_load_internal::file::load_multi_threaded::{{closure}}::{{closure}}::h16d5eba0a83616fc
at /home/jan/_code/_roc/roc/crates/compiler/load_internal/src/file.rs:1659:25
32: 0x5635b744caf7 - crossbeam_utils::thread::ScopedThreadBuilder::spawn::{{closure}}::ha411c1ac2e878ff5
at /home/jan/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-utils-0.8.10/src/thread.rs:438:31
33: 0x5635b73d3fef - core::ops::function::FnOnce::call_once{{vtable.shim}}::h182bc693cee28e90
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/core/src/ops/function.rs:227:5
34: 0x5635b740505d - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::hee34075471a3830c
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/alloc/src/boxed.rs:1853:9
35: 0x5635b74eed84 - std::sys_common::backtrace::__rust_begin_short_backtrace::h74784c32857fa8b7
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/sys_common/backtrace.rs:122:18
36: 0x5635b74dd8d4 - std::thread::Builder::spawn_unchecked_::{{closure}}::{{closure}}::h54bd13ac090e0e91
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/thread/mod.rs:498:17
37: 0x5635b74efaa4 - <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once::he1cf1df6def34f88
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/core/src/panic/unwind_safe.rs:271:9
38: 0x5635b74ef26d - std::panicking::try::do_call::h48b63b9234108830
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/panicking.rs:492:40
39: 0x5635b74ef49b - __rust_try
40: 0x5635b74ef1bd - std::panicking::try::hb3ba778589fab460
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/panicking.rs:456:19
41: 0x5635b74ec7d4 - std::panic::catch_unwind::h7c5138398b54d137
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/panic.rs:137:14
42: 0x5635b74dd6f3 - std::thread::Builder::spawn_unchecked_::{{closure}}::hd3349d142c8673c4
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/thread/mod.rs:497:30
43: 0x5635b73d3f0f - core::ops::function::FnOnce::call_once{{vtable.shim}}::h14f5d8c93db30fba
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/core/src/ops/function.rs:227:5
44: 0x5635ba329d03 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::hf70ac038171e3e1a
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/alloc/src/boxed.rs:1853:9
45: 0x5635ba329d03 - <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once::he6690128792365ad
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/alloc/src/boxed.rs:1853:9
46: 0x5635ba329d03 - std::sys::unix::thread::Thread::new::thread_start::ha07928d93d5a5ec9
at /rustc/7737e0b5c4103216d6fd8cf941b7ab9bdbaace7c/library/std/src/sys/unix/thread.rs:108:17
47: 0x7f8513f29d40 - start_thread
48: 0x7f8513a8703f - __GI___clone
49: 0x0 - <unknown>
^C
[jan@framey roc]$
does it pass roc check
?
That catches a lot that might crash the compiler.
GAH I always forget to go back to checking
I do check check check check, then the first time it passes I go straight to run run run run while editing :facepalm:
Thanks again, check has my answers :)
that's surprising actually - cargo run
should do the "check
and then only run if there are no errors reported" flow :thinking:
so I'm surprised that cargo run ../domain-modeling-in-roc/step_2__card_game_A.roc
would report no compile-time errors but cargo run check
would!
really? I thought cargo run <script>
and roc <script>
did not run check. That would definitely be useful.
so roc foo.roc
does this, but roc run foo.roc
just runs and doesn't check
hmm. Yeah, I don't think that is working
so it's the difference between cargo run run
and cargo run
:big_smile:
Or maybe it does part of the check but not all of it...not sure
I definitely need to use check
still.
hm, if you can reproduce an example of it not working right, maybe it's an easy fix!
Hello, it's me again, Dr. Help I. M. Crashing :laughing:
This behavior seems like it shouldn't be possible, unless there's a bug in the release build process:
[jan@framey ~]$ cd ~/_code/_roc/roc
[jan@framey roc]$
[jan@framey roc]$ nix develop
[jan@framey roc]$
[jan@framey roc]$ cargo run ../project-euler-in-roc/2.roc
Finished dev [unoptimized + debuginfo] target(s) in 0.46s
Running `target/debug/roc ../project-euler-in-roc/2.roc`
🔨 Rebuilding host...
4613732
[jan@framey roc]$
[jan@framey roc]$ cargo build --release
Finished release [optimized] target(s) in 0.48s
[jan@framey roc]$
[jan@framey roc]$ exit
exit
[jan@framey roc]$
[jan@framey roc]$ cd ~/_code/_roc/project-euler-in-roc/
[jan@framey project-euler-in-roc]$
[jan@framey project-euler-in-roc]$ ../roc/target/release/roc 2.roc
🔨 Rebuilding host...
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', crates/compiler/build/src/link.rs:360:22
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Any { .. }', crates/cli/src/build.rs:283:46
[jan@framey project-euler-in-roc]$
[jan@framey project-euler-in-roc]$ ../roc/target/release/roc check 2.roc
0 errors and 0 warnings found in 20 ms.
[jan@framey project-euler-in-roc]$
[jan@framey project-euler-in-roc]$ ???
It seems to not matter which app I run, the same "yes inside nix develop
, no outside it" happens
Did I miss a step? (I remembered to check
this time! haha)
(on Linux, Fedora 36)
Oh, and I guess this is additional info + a more concise repro:
[jan@framey roc]$ ./target/release/roc ../project-euler-in-roc/2.roc
🔨 Rebuilding host...
4613732
[jan@framey roc]$
[jan@framey roc]$ exit
exit
[jan@framey roc]$
[jan@framey roc]$ cd ../project-euler-in-roc/
[jan@framey project-euler-in-roc]$
[jan@framey project-euler-in-roc]$ ../roc/target/release/roc 2.roc
🔨 Rebuilding host...
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', crates/compiler/build/src/link.rs:360:22
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Any { .. }', crates/cli/src/build.rs:283:46
[jan@framey project-euler-in-roc]$
Summary:
cargo run myApp.roc
works inside nix develop
cargo build --release
works inside nix develop
target/release/roc myApp.roc
works inside nix develop
target/release/roc myApp.roc
panics outside nix develop
I thought builds were self-contained, but are they not?
Your missing zig on your machine?
*line 360 not 310, so clang
You can't build the platform
If you were to build in nix and then build outside of nix with --precompiled-host=true
, it should work.
We should improve those error messages
:check: Wow, that was fast.
[jan@framey project-euler-in-roc]$ ../roc/target/release/roc 2.roc --precompiled-host=true
4613732
[jan@framey project-euler-in-roc]$
I didn't know about that flag, and I assumed a Roc build could compile platforms/hosts without any system dependencies!
The host is still in another language, so that has all the dependencies that other language brings in. Long term, you should be able to just download the package as a precompiled host. Once that is the norm, all the host building shouldn't matter to anyone except platform authors.
Nice! So right now, the --precompiled-host=true
tells the Roc executable to look in the platform dir for a platform executable?
Should the word "host" be replaced with "platform" in all app-developer-facing messaging?
It basically says, trust me, the platform was already compiled and has not changed. Don't spawn the thread that compiles the platform. We will use the existing executable file.
And probably to the host
vs platform
question. It is a terminology thing that has turned into them essentially being used interchangeably (though there is technically a difference). Platform is definitely the term I would expect an app author to see and use.
Great! Thank you very much for these great explanations :)
This will definitely become part of my workflow:
[jan@framey project-euler-in-roc]$ alias roc='../roc/target/release/roc --precompiled-ho
st=true'
[jan@framey project-euler-in-roc]$ roc 2.roc
4613732
[jan@framey project-euler-in-roc]$
Regarding host
vs. platform
: https://github.com/rtfeldman/roc/issues/3630
Regarding panic/error messaging: https://github.com/rtfeldman/roc/issues/3631
On terminology: The platform consists of a non-Roc "host" and a Roc API to interface to it.
So the message is referring to technically the right thing, but using a term that isn't explained anywhere in the docs yet.
Sure, but to a roc app author, they will only ever interact with platforms as a unit, never the host directly. So flags and error messages should generally be catered to that?
At least that is my thought
I wasn't disagreeing, just explaining to anyone following who didn't know.
Nice, does that API include only .roc
files?
Yes
How do I bool?
|> List.map (\sum ->
when sum * 2 > rowCount is
Bool.true -> 1
_ -> 0)
── SYNTAX PROBLEM ──────────────────────────────────────── day_3_solutions.roc ─
This pattern is malformed:
37│ Bool.true -> 1
^^^^^^^^^
It works as an if ... then ... else ...
expression but not a when ... is ...
expression
I'll just switch it to if/else
Dang functional programming... making me forget about good old-fashioned if
/else
... :angry_cat: :laughing:
oh yeah, this is supposed to work exactly the way you tried to write it - it just hasn't been implemened yet :sweat_smile:
Foo.bar -> ...
should desugar to essentially x if x == Foo.bar -> ...
which you can always write by hand, but yes - in the case of booleans specifically, might as well use if
! :big_smile:
Any hope for [a, b, c] -> ...
? :fingers_crossed: :present: :santa:
Maybe tuples will alleviate that need
yeah there was some discussion on Zulip about that awhile back
I think some form of it will turn out to be a good idea!
@JanCVanB: In this topic, they show how to pattern match in the way you were looking for.
Last updated: Jul 06 2025 at 12:14 UTC