is there some way to accomplish this?
contents <- path
|> Path.fromStr
|> File.readUtf8
|> Task.onFail \_ -> Stderr.line "oops" |> Task.await
i get the errors
── TOO MANY ARGS ────────────────────────────────────── platform-test/main.roc ─
The onFail function expects 2 arguments, but it got 3 instead:
20│ contents <- path |> Path.fromStr |> File.readUtf8 |> Task.onFail \_ -> Stderr.line "oops" |> Task.await
^^^^^^^^^^^
Are there any missing commas? Or missing parentheses?
── TOO FEW ARGS ─────────────────────────────────────── platform-test/main.roc ─
The await function expects 2 arguments, but it got only 1:
20│ contents <- path |> Path.fromStr |> File.readUtf8 |> Task.onFail \_ -> Stderr.line "oops" |> Task.await
^^^^^^^^^^
i'm thinking Task.attempt
might work
Syntax aside, I don't think stderr writing should pipe to a variable named contents... mixing steps?
Today you'll need to add parentheses around your mid-pipeline function
What type are you aiming for for contents?
contents is a List u8
i believe
wait maybe its a Str. next line is:
raw = Str.toUtf8 contents
(there is a File.readBytes that you might like)
But yes, that sounds like a good aim, for contents to be a str
yeah i don't really care what type contents is, Str or List u8 is fine. but lets just say Str for now
Unfortunately, after File.readUtf8 returns a task that could Ok or Err, you're not handling/extracting either the Ok or Err cases
this is just for demoing my zig platform
I recommend changing contents
to result
deleting the onFail logic and just awaiting the file read
then on a new line handle the result
ok sounds good. i'll see what i can do.
thanks!
After that works, recombining the lines is still an option but you'll have a working baseline :)
You bet!
And in case it's a point of confusion, stderr writing isn't a side effect
hold on. how can i get a Result without stopping the subsequent chain of Tasks? Task.attempt?
thats what i was trying to use but having some problems. does it sound like it should work?
maybe you are saying instead of backpassing handle contents in a new function?
Shouldn't this work?
contents <- path
|> Path.fromStr
|> File.readUtf8
|> Task.onFail (\_ -> Stderr.line "oops")
|> Task.await
ooh seems closer. now i get an unexpected type error...
the diff is Task {}
vs Task Str
This 2nd argument to onFail has an unexpected type:
31│ |> Task.onFail (\_ -> Stderr.line "oops")
^^^^^^^^^^^^^^^^^^^^^^^^
The argument is an anonymous function of type:
[FileReadErr Path ReadErr,
FileReadUtf8Err Path [BadUtf8 Utf8ByteProblem Nat]*]* ->
Task {} * [Read [File]*, Write [Stderr]*]a ?
But onFail needs its 2nd argument to be:
[FileReadErr Path ReadErr,
FileReadUtf8Err Path [BadUtf8 Utf8ByteProblem Nat]*]* ->
Task Str * [Read [File]*, Write [Stderr]*]a ?
last line and 4th from last
seems i just need to return a string there
Ah, onFail
expects you to return a task that gives you a new value for contents. So a Str
So this would work, but doesn't print of coures
contents <- path
|> Path.fromStr
|> File.readUtf8
|> Task.onFail (\_ -> Task.succeed "Some backup content value")
|> Task.await
ah neat. well atleast i'll be able to see that an error happened. this will work ok for my use case.
thank you both :+1:
So the current implementation of onFail
doesn't work the way we expected here.
I think you would need to do:
contents <- path
|> Path.fromStr
|> File.readUtf8
|> Task.onFail (\e ->
_ <- Stderr.line "oops" |> Task.await
Task.fail e
)
|> Task.await
Probably would want a different helper function
So I guess onFail
is really "if I fail, try to recover and continue by doing this"
You want, "if I fail, do this, but stay as a failure"
This is probably worth making an issue for, or at least a discussion in #ideas. Seems like a generally useful Task function that we are missing. Also a possible naming confusion/conflict.
yes. it would be nice to have a helper for that. i agree.
maybe ill post some of this in ideas in a bit.
Travis has marked this topic as resolved.
That last code snippet works if the file exists (the Ok
case), but it panics if it doesn't (the Err
case):
thread 'main' panicked at 'not yet implemented: Report a file open error', src/lib.rs:375:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5
Aborted (core dumped)
Oh, that's not a roc issue, that's a rust platform issue.
The platform doesn't handle all errors yet
So some cases it just panics
Oh... Oh! I just now understood the "stderr quick exit" approach you two are both taking... that's cool.
oh reeeeaaalllly? :big_smile: i think i was able to get it working in my zig platform
$roc platform-test/main.roc -- asfd
🔨 Rebuilding platform...
----
File.readUtf8 platform-test/main.roc:
----
app "platform-main"
packages { ...(truncated)
----
not-a-file: Not Found
like just got it working this morning doing some error handling in roc_fx_fileReadBytes
I never realized you could write a backpassing chain that handles early exits halfway through...
That's great!
I mean the pipelining is handling the early exits. The backpass once triggered would be too late.
That said, it is also valid to just accumulate the errors and handle them all at the end in one place.
happy <-
input
|> process
|> ifSad exitSadly
|> await
smile <-
happy
|> react
|> ifFrown goToFrownTown
|> await
smile
|> welcomeToSmileTown
Brendan Hansknecht said:
That said, it is also valid to just accumulate the errors and handle them all at the end in one place.
Related to this, if you accumulate all errors, how can you avoid error Tag naming conflict from different lib/package?
@Brendan Hansknecht I credit backpassing with this "early escape" functionality, because if you only have |>
s with no <-
then at the end of the pipeline you'll have to handle a complex multi-type for all possible outcomes.
...hence why I was going to suggest changing contents
to result
and then doing the Err
handling after.
With <-
, you can add mid-pipeline handler steps to narrow the type "returned" by <-
, because you're essentially narrowing the branches that the final callback must handle.
This might be understood by everyone else here, but it's news to me :D
related to this, is there a way to get the tag name as a string? or would i just have to do a manual switch?
|> Task.onFail (\_ -> Task.succeed "Not Found") #TODO print error tag somehow
your would just add a lambda to the last await in the pipeline, it would be the same thing with more indentation.
manual switch
Tag naming conflict from different lib/package?
Wrap them by package or function or etc
i think i'm spoiled by zig here
haha. Sounds reasonable. Zig is a nice language.
yeah @tagName would be nice here
That's something else that could be worth putting in the #ideas stream.
Last updated: Jul 06 2025 at 12:14 UTC