diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 0b4330dca9..abb58f7a65 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -1056,50 +1056,29 @@ mod cli_run { &[], indoc!( r#" - ── TYPE MISMATCH ─ ...known_bad/../../../../examples/cli/cli-platform/main.roc ─ + ── TYPE MISMATCH ─────────────────────────────── tests/known_bad/TypeError.roc ─ - Something is off with the type annotation of the main required symbol: + This 2nd argument to attempt has an unexpected type: - 2│ requires {} { main : InternalProgram } - ^^^^^^^^^^^^^^^ + 15│> Task.attempt task /result -> + 16│> when result is + 17│> Ok {} -> Stdout.line "Done!" + 18│> # Type mismatch because the File.readUtf8 error case is not handled + 19│> Err {} -> Stdout.line "Problem!" - This #UserApp.main value is a: + The argument is an anonymous function of type: - Task.Task {} * [Write [Stdout]] + [Err {}a, Ok {}] -> Task {} * - But the type annotation on main says it should be: + But attempt needs its 2nd argument to be: - InternalProgram.InternalProgram - - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. - - - ── TYPE MISMATCH ─ ...known_bad/../../../../examples/cli/cli-platform/main.roc ─ - - This 1st argument to toEffect has an unexpected type: - - 9│ mainForHost = InternalProgram.toEffect main - ^^^^ - - This #UserApp.main value is a: - - Task.Task {} * [Write [Stdout]] - - But toEffect needs its 1st argument to be: - - InternalProgram.InternalProgram - - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. + Result {} [FileReadErr Path.Path InternalFile.ReadErr, + FileReadUtf8Err Path.Path [BadUtf8 Utf8ByteProblem Nat]*]* -> + Task {} * ──────────────────────────────────────────────────────────────────────────────── - 2 errors and 1 warning found in ms."# + 1 error and 0 warnings found in ms."# ), ); } diff --git a/crates/cli/tests/known_bad/TypeError.roc b/crates/cli/tests/known_bad/TypeError.roc index 0d7e15f6d7..c07a8fb773 100644 --- a/crates/cli/tests/known_bad/TypeError.roc +++ b/crates/cli/tests/known_bad/TypeError.roc @@ -1,13 +1,19 @@ app "type-error" packages { pf: "../../../../examples/cli/cli-platform/main.roc" } - imports [pf.Stdout.{ line }, pf.Task.{ await }, pf.Program] + imports [pf.Stdout.{ line }, pf.Task.{ await }, pf.Path, pf.File] provides [main] to pf main = - _ <- await (line "a") - _ <- await (line "b") - _ <- await (line "c") - _ <- await (line "d") - line "e" - # Type mismatch because this line is missing: - # |> Program.quick + task = + _ <- await (line "a") + _ <- await (line "b") + _ <- await (line "c") + _ <- await (line "d") + _ <- await (File.readUtf8 (Path.fromStr "blah.txt")) + line "e" + + Task.attempt task \result -> + when result is + Ok {} -> Stdout.line "Done!" + # Type mismatch because the File.readUtf8 error case is not handled + Err {} -> Stdout.line "Problem!" diff --git a/examples/cli/.gitignore b/examples/cli/.gitignore index 8e260e400a..7df3b84374 100644 --- a/examples/cli/.gitignore +++ b/examples/cli/.gitignore @@ -7,3 +7,4 @@ tui http-get file-io env +out.txt \ No newline at end of file diff --git a/examples/cli/args.roc b/examples/cli/args.roc index 50f4ca08a3..9f9215840f 100644 --- a/examples/cli/args.roc +++ b/examples/cli/args.roc @@ -1,10 +1,11 @@ app "args" packages { pf: "cli-platform/main.roc" } - imports [pf.Stdout, pf.Arg, pf.Program.{ Program }] + imports [pf.Stdout, pf.Arg, pf.Task.{ Task }, pf.Process] provides [main] to pf -main : Program -main = Program.withArgs \args -> +main : Task {} [] +main = + args <- Arg.list |> Task.await parser = divCmd = Arg.succeed (\dividend -> \divisor -> Div (Num.toF64 dividend) (Num.toF64 divisor)) @@ -53,11 +54,10 @@ main = Program.withArgs \args -> runCmd cmd |> Num.toStr |> Stdout.line - |> Program.exit 0 Err helpMenu -> - Stdout.line helpMenu - |> Program.exit 1 + {} <- Stdout.line helpMenu |> Task.await + Process.exit 1 runCmd = \cmd -> when cmd is diff --git a/examples/cli/cli-platform/Arg.roc b/examples/cli/cli-platform/Arg.roc index 352f88ad85..34398bf315 100644 --- a/examples/cli/cli-platform/Arg.roc +++ b/examples/cli/cli-platform/Arg.roc @@ -14,8 +14,16 @@ interface Arg choice, withParser, program, + list, ] - imports [] + imports [Effect, InternalTask, Task.{ Task }] + +## Gives a list of the program's command-line arguments. +list : Task (List Str) * +list = + Effect.args + |> Effect.map Ok + |> InternalTask.fromEffect ## A parser for a command-line application. ## A [NamedParser] is usually built from a [Parser] using [program]. @@ -695,7 +703,7 @@ formatError = \err -> |> Str.joinWith ", " """ - The \(fmtFound) subcommand was found, but it's not expected in this context! + The \(fmtFound) subcommand was found, but it's not expected in this context! The available subcommands are: \t\(fmtChoices) """ @@ -1025,7 +1033,7 @@ expect err == """ - The "logs" subcommand was found, but it's not expected in this context! + The "logs" subcommand was found, but it's not expected in this context! The available subcommands are: \t"auth", "publish" """ diff --git a/examples/cli/cli-platform/Dir.roc b/examples/cli/cli-platform/Dir.roc index 73741ef035..a58d091ad8 100644 --- a/examples/cli/cli-platform/Dir.roc +++ b/examples/cli/cli-platform/Dir.roc @@ -9,7 +9,7 @@ DeleteErr : InternalDir.DeleteErr DirEntry : InternalDir.DirEntry ## Lists the files and directories inside the directory. -list : Path -> Task (List Path) [DirReadErr Path ReadErr] [Read [File]] +list : Path -> Task (List Path) [DirReadErr Path ReadErr] list = \path -> effect = Effect.map (Effect.dirList (InternalPath.toBytes path)) \result -> when result is @@ -19,7 +19,7 @@ list = \path -> InternalTask.fromEffect effect ## Deletes a directory if it's empty. -deleteEmptyDir : Path -> Task {} [DirDeleteErr Path DeleteErr] [Write [File]] +deleteEmptyDir : Path -> Task {} [DirDeleteErr Path DeleteErr] ## Recursively deletes the directory as well as all files and directories inside it. -deleteRecursive : Path -> Task {} [DirDeleteErr Path DeleteErr] [Write [File]] +deleteRecursive : Path -> Task {} [DirDeleteErr Path DeleteErr] diff --git a/examples/cli/cli-platform/Effect.roc b/examples/cli/cli-platform/Effect.roc index 9d20093613..20d88e59c7 100644 --- a/examples/cli/cli-platform/Effect.roc +++ b/examples/cli/cli-platform/Effect.roc @@ -23,6 +23,7 @@ hosted Effect fileDelete, fileWriteUtf8, fileWriteBytes, + processExit, ] imports [InternalHttp.{ Request, Response }, InternalFile, InternalDir] generates Effect with [after, map, always, forever, loop] @@ -43,6 +44,8 @@ envVar : Str -> Effect (Result Str {}) exePath : Effect (Result (List U8) {}) setCwd : List U8 -> Effect (Result {} {}) +processExit : U8 -> Effect {} + # If we encounter a Unicode error in any of the args, it will be replaced with # the Unicode replacement char where necessary. args : Effect (List Str) diff --git a/examples/cli/cli-platform/Env.roc b/examples/cli/cli-platform/Env.roc index c5dd393083..2826d34941 100644 --- a/examples/cli/cli-platform/Env.roc +++ b/examples/cli/cli-platform/Env.roc @@ -4,7 +4,7 @@ interface Env ## Reads the [current working directory](https://en.wikipedia.org/wiki/Working_directory) ## from the environment. File operations on relative [Path]s are relative to this directory. -cwd : Task Path [CwdUnavailable] [Read [Env]] +cwd : Task Path [CwdUnavailable] cwd = effect = Effect.map Effect.cwd \bytes -> if List.isEmpty bytes then @@ -17,14 +17,14 @@ cwd = ## Sets the [current working directory](https://en.wikipedia.org/wiki/Working_directory) ## in the environment. After changing it, file operations on relative [Path]s will be relative ## to this directory. -setCwd : Path -> Task {} [InvalidCwd] [Write [Env]] +setCwd : Path -> Task {} [InvalidCwd] setCwd = \path -> Effect.setCwd (InternalPath.toBytes path) |> Effect.map (\result -> Result.mapErr result \{} -> InvalidCwd) |> InternalTask.fromEffect ## Gets the path to the currently-running executable. -exePath : Task Path [ExePathUnavailable] [Read [Env]] +exePath : Task Path [ExePathUnavailable] exePath = effect = Effect.map Effect.exePath \result -> @@ -39,7 +39,7 @@ exePath = ## If the value is invalid Unicode, the invalid parts will be replaced with the ## [Unicode replacement character](https://unicode.org/glossary/#replacement_character) ## (`�`). -var : Str -> Task Str [VarNotFound] [Read [Env]] +var : Str -> Task Str [VarNotFound] var = \name -> Effect.envVar name |> Effect.map (\result -> Result.mapErr result \{} -> VarNotFound) @@ -65,7 +65,7 @@ var = \name -> ## - comma-separated lists (of either strings or numbers), as long as there are no spaces after the commas ## ## Trying to decode into any other types will always fail with a `DecodeErr`. -decode : Str -> Task val [VarNotFound, DecodeErr DecodeError] [Read [Env]] | val has Decoding +decode : Str -> Task val [VarNotFound, DecodeErr DecodeError] | val has Decoding decode = \name -> Effect.envVar name |> Effect.map @@ -83,7 +83,7 @@ decode = \name -> ## ## If any key or value contains invalid Unicode, the [Unicode replacement character](https://unicode.org/glossary/#replacement_character) ## (`�`) will be used in place of any parts of keys or values that are invalid Unicode. -dict : Task (Dict Str Str) * [Read [Env]] +dict : Task (Dict Str Str) * dict = Effect.envDict |> Effect.map Ok diff --git a/examples/cli/cli-platform/File.roc b/examples/cli/cli-platform/File.roc index ee9136e34b..708fe1b7a6 100644 --- a/examples/cli/cli-platform/File.roc +++ b/examples/cli/cli-platform/File.roc @@ -26,7 +26,7 @@ WriteErr : InternalFile.WriteErr ## This opens the file first and closes it after writing to it. ## ## To write unformatted bytes to a file, you can use [File.writeBytes] instead. -write : Path, val, fmt -> Task {} [FileWriteErr Path WriteErr] [Write [File]] | val has Encode.Encoding, fmt has Encode.EncoderFormatting +write : Path, val, fmt -> Task {} [FileWriteErr Path WriteErr] | val has Encode.Encoding, fmt has Encode.EncoderFormatting write = \path, val, fmt -> bytes = Encode.toBytes val fmt @@ -41,7 +41,7 @@ write = \path, val, fmt -> ## This opens the file first and closes it after writing to it. ## ## To format data before writing it to a file, you can use [File.write] instead. -writeBytes : Path, List U8 -> Task {} [FileWriteErr Path WriteErr] [Write [File]] +writeBytes : Path, List U8 -> Task {} [FileWriteErr Path WriteErr] writeBytes = \path, bytes -> toWriteTask path \pathBytes -> Effect.fileWriteBytes pathBytes bytes @@ -53,7 +53,7 @@ writeBytes = \path, bytes -> ## This opens the file first and closes it after writing to it. ## ## To write unformatted bytes to a file, you can use [File.writeBytes] instead. -writeUtf8 : Path, Str -> Task {} [FileWriteErr Path WriteErr] [Write [File]] +writeUtf8 : Path, Str -> Task {} [FileWriteErr Path WriteErr] writeUtf8 = \path, str -> toWriteTask path \bytes -> Effect.fileWriteUtf8 bytes str @@ -73,7 +73,7 @@ writeUtf8 = \path, str -> ## ## On Windows, this will fail when attempting to delete a readonly file; the file's ## readonly permission must be disabled before it can be successfully deleted. -delete : Path -> Task {} [FileWriteErr Path WriteErr] [Write [File]] +delete : Path -> Task {} [FileWriteErr Path WriteErr] delete = \path -> toWriteTask path \bytes -> Effect.fileDelete bytes @@ -85,7 +85,7 @@ delete = \path -> ## This opens the file first and closes it after reading its contents. ## ## To read and decode data from a file, you can use `File.read` instead. -readBytes : Path -> Task (List U8) [FileReadErr Path ReadErr] [Read [File]] +readBytes : Path -> Task (List U8) [FileReadErr Path ReadErr] readBytes = \path -> toReadTask path \bytes -> Effect.fileReadBytes bytes @@ -100,10 +100,7 @@ readBytes = \path -> ## To read unformatted bytes from a file, you can use [File.readBytes] instead. readUtf8 : Path - -> Task - Str - [FileReadErr Path ReadErr, FileReadUtf8Err Path _] - [Read [File]] + -> Task Str [FileReadErr Path ReadErr, FileReadUtf8Err Path _] readUtf8 = \path -> effect = Effect.map (Effect.fileReadBytes (InternalPath.toBytes path)) \result -> when result is @@ -132,14 +129,14 @@ readUtf8 = \path -> # Err decodingErr -> Err (FileReadDecodeErr decodingErr) # Err readErr -> Err (FileReadErr readErr) # InternalTask.fromEffect effect -toWriteTask : Path, (List U8 -> Effect (Result ok err)) -> Task ok [FileWriteErr Path err] [Write [File]] +toWriteTask : Path, (List U8 -> Effect (Result ok err)) -> Task ok [FileWriteErr Path err] toWriteTask = \path, toEffect -> InternalPath.toBytes path |> toEffect |> InternalTask.fromEffect |> Task.mapFail \err -> FileWriteErr path err -toReadTask : Path, (List U8 -> Effect (Result ok err)) -> Task ok [FileReadErr Path err] [Read [File]] +toReadTask : Path, (List U8 -> Effect (Result ok err)) -> Task ok [FileReadErr Path err] toReadTask = \path, toEffect -> InternalPath.toBytes path |> toEffect diff --git a/examples/cli/cli-platform/Http.roc b/examples/cli/cli-platform/Http.roc index 78685ba131..4e39bad419 100644 --- a/examples/cli/cli-platform/Http.roc +++ b/examples/cli/cli-platform/Http.roc @@ -103,7 +103,7 @@ errorToString = \err -> BadStatus code -> Str.concat "Request failed with status " (Num.toStr code) BadBody details -> Str.concat "Request failed. Invalid body. " details -send : Request -> Task Str Error [Network [Http]] +send : Request -> Task Str Error send = \req -> # TODO: Fix our C ABI codegen so that we don't this Box.box heap allocation Effect.sendRequest (Box.box req) diff --git a/examples/cli/cli-platform/InternalProgram.roc b/examples/cli/cli-platform/InternalProgram.roc deleted file mode 100644 index 25ef2989b4..0000000000 --- a/examples/cli/cli-platform/InternalProgram.roc +++ /dev/null @@ -1,11 +0,0 @@ -interface InternalProgram - exposes [InternalProgram, fromEffect, toEffect] - imports [Effect.{ Effect }] - -InternalProgram := Effect U8 - -fromEffect : Effect U8 -> InternalProgram -fromEffect = @InternalProgram - -toEffect : InternalProgram -> Effect U8 -toEffect = \@InternalProgram effect -> effect diff --git a/examples/cli/cli-platform/InternalTask.roc b/examples/cli/cli-platform/InternalTask.roc index 3c25382b35..5fd5804003 100644 --- a/examples/cli/cli-platform/InternalTask.roc +++ b/examples/cli/cli-platform/InternalTask.roc @@ -2,16 +2,16 @@ interface InternalTask exposes [Task, fromEffect, toEffect, succeed, fail] imports [Effect.{ Effect }] -Task ok err fx := Effect (Result ok err) +Task ok err := Effect (Result ok err) -succeed : ok -> Task ok * * +succeed : ok -> Task ok * succeed = \ok -> @Task (Effect.always (Ok ok)) -fail : err -> Task * err * +fail : err -> Task * err fail = \err -> @Task (Effect.always (Err err)) -fromEffect : Effect (Result ok err) -> Task ok err * +fromEffect : Effect (Result ok err) -> Task ok err fromEffect = \effect -> @Task effect -toEffect : Task ok err * -> Effect (Result ok err) +toEffect : Task ok err -> Effect (Result ok err) toEffect = \@Task effect -> effect diff --git a/examples/cli/cli-platform/Process.roc b/examples/cli/cli-platform/Process.roc new file mode 100644 index 0000000000..b8d8a0551f --- /dev/null +++ b/examples/cli/cli-platform/Process.roc @@ -0,0 +1,13 @@ +interface Process + exposes [exit] + imports [Task.{ Task }, InternalTask, Effect] + +## Exit the process with +## +## {} <- Stderr.line "Exiting right now!" |> Task.await +## Process.exit 1 +exit : U8 -> Task {} * +exit = \code -> + Effect.processExit code + |> Effect.map \_ -> Ok {} + |> InternalTask.fromEffect diff --git a/examples/cli/cli-platform/Program.roc b/examples/cli/cli-platform/Program.roc deleted file mode 100644 index 7cefc46f0c..0000000000 --- a/examples/cli/cli-platform/Program.roc +++ /dev/null @@ -1,109 +0,0 @@ -interface Program - exposes [Program, ExitCode, noArgs, withArgs, quick, withEnv, exitCode, exit] - imports [Task.{ Task }, InternalProgram.{ InternalProgram }, InternalTask, Effect] - -## A [command-line interface](https://en.wikipedia.org/wiki/Command-line_interface) program. -Program : InternalProgram - -## An [exit status](https://en.wikipedia.org/wiki/Exit_status) code. -ExitCode := U8 - -## Converts a [U8] to an [ExitCode]. -## -## If you already have a [Task] and want to convert its success type -## from `{}` to [ExitCode], you may find [Program.exit] convenient. -exitCode : U8 -> ExitCode -exitCode = @ExitCode - -## Attach an [ExitCode] to a task. -## -## Stderr.line "I hit an error and couldn't continue." -## |> Program.exit 1 -## -## Note that this does not terminate the current process! By design, this platform does not have -## a [Task] which terminates the current process. Instead, error handling should be consistently -## done through task failures. -## -## To convert a [U8] directly into an [ExitCode], use [Program.exitCode]. -exit : Task {} [] fx, U8 -> Task ExitCode [] fx -exit = \task, code -> - Task.map task \{} -> @ExitCode code - -## A program which runs the given task and discards the values it produces on success or failure. -## One use for this is as an introductory [Program] when teaching someone how to use this platform. -## -## If the task succeeds, the program will exit with a [status](https://en.wikipedia.org/wiki/Exit_status) -## of 0. If the task fails, the program will exit with a status of 1. -## If the task crashes, the program will exit with a status of 2. -## -## For a similar program which specifies its exit status explicitly, see [Program.noArgs]. -quick : Task * * * -> Program -quick = \task -> - effect = - InternalTask.toEffect task - |> Effect.map \result -> - when result is - Ok _ -> 0 - Err _ -> 1 - - InternalProgram.fromEffect effect - -## A program which uses no [command-line arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments) -## and specifies an [exit status](https://en.wikipedia.org/wiki/Exit_status) [U8]. -## -## Note that the task's failure type must be `[]`. You can satisfy that by handling all -## the task's potential failures using something like [Task.attempt]. -## -## For a similar program which does use command-line arguments, see [Program.withArgs]. -noArgs : Task ExitCode [] * -> Program -noArgs = \task -> - effect = - InternalTask.toEffect task - |> Effect.map \Ok (@ExitCode u8) -> u8 - - InternalProgram.fromEffect effect - -## A program which uses [command-line arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments) -## and specifies an [exit status](https://en.wikipedia.org/wiki/Exit_status) [U8]. -## -## Note that the task's failure type must be `[]`. You can satisfy that by handling all -## the task's potential failures using something like [Task.attempt]. -## -## If any command-line arguments contain invalid Unicode, the invalid parts will be replaced with -## the [Unicode replacement character](https://unicode.org/glossary/#replacement_character) -## (`�`). -withArgs : (List Str -> Task ExitCode [] *) -> Program -withArgs = \toTask -> - effect = Effect.after Effect.args \args -> - toTask args - |> InternalTask.toEffect - |> Effect.map \Ok (@ExitCode u8) -> u8 - - InternalProgram.fromEffect effect - -## A program which uses [command-line arguments](https://en.wikipedia.org/wiki/Command-line_interface#Arguments) -## and a dictionary of [environment variables](https://en.wikipedia.org/wiki/Environment_variable). -## -## This is a combination of [Program.withArgs] and `Env.dict`. Note that the task's failure type -## must be `[]`. You can satisfy that by handling all the task's potential failures using -## something like [Task.attempt]. -## -## If any command-line arguments contain invalid Unicode, the invalid parts will be replaced with -## the [Unicode replacement character](https://unicode.org/glossary/#replacement_character) -## (`�`). -withEnv : (List Str, Dict Str Str -> Task ExitCode [] *) -> Program -withEnv = \toTask -> - effect = - args <- Effect.args |> Effect.after - dict <- Effect.envDict |> Effect.after - - toTask args dict - |> InternalTask.toEffect - |> Effect.map \Ok (@ExitCode u8) -> u8 - - InternalProgram.fromEffect effect - -# ## A combination of [Program.withArgs] and [Env.decodeAll], with the output of [Env.decodeAll] -# ## being passed after the command-line arguments. -# decodedEnv : (List Str, Result env [EnvDecodingFailed Str]* -> Task U8 [] *) -> Program -# | env has Decode diff --git a/examples/cli/cli-platform/Stderr.roc b/examples/cli/cli-platform/Stderr.roc index db7a75146b..557a0a21a9 100644 --- a/examples/cli/cli-platform/Stderr.roc +++ b/examples/cli/cli-platform/Stderr.roc @@ -2,13 +2,13 @@ interface Stderr exposes [line, write] imports [Effect, Task.{ Task }, InternalTask] -line : Str -> Task {} * [Write [Stderr]] +line : Str -> Task {} * line = \str -> Effect.stderrLine str |> Effect.map (\_ -> Ok {}) |> InternalTask.fromEffect -write : Str -> Task {} * [Write [Stderr]] +write : Str -> Task {} * write = \str -> Effect.stderrWrite str |> Effect.map (\_ -> Ok {}) diff --git a/examples/cli/cli-platform/Stdin.roc b/examples/cli/cli-platform/Stdin.roc index 961b578a11..f3a6124f53 100644 --- a/examples/cli/cli-platform/Stdin.roc +++ b/examples/cli/cli-platform/Stdin.roc @@ -2,7 +2,7 @@ interface Stdin exposes [line] imports [Effect, Task.{ Task }, InternalTask] -line : Task Str * [Read [Stdin]] +line : Task Str * line = Effect.stdinLine |> Effect.map Ok diff --git a/examples/cli/cli-platform/Stdout.roc b/examples/cli/cli-platform/Stdout.roc index c8793c776c..0b651563ec 100644 --- a/examples/cli/cli-platform/Stdout.roc +++ b/examples/cli/cli-platform/Stdout.roc @@ -2,13 +2,13 @@ interface Stdout exposes [line, write] imports [Effect, Task.{ Task }, InternalTask] -line : Str -> Task {} * [Write [Stdout]] +line : Str -> Task {} * line = \str -> Effect.stdoutLine str |> Effect.map (\_ -> Ok {}) |> InternalTask.fromEffect -write : Str -> Task {} * [Write [Stdout]] +write : Str -> Task {} * write = \str -> Effect.stdoutWrite str |> Effect.map (\_ -> Ok {}) diff --git a/examples/cli/cli-platform/Task.roc b/examples/cli/cli-platform/Task.roc index c5f439d60a..e350559665 100644 --- a/examples/cli/cli-platform/Task.roc +++ b/examples/cli/cli-platform/Task.roc @@ -2,9 +2,9 @@ interface Task exposes [Task, succeed, fail, await, map, mapFail, onFail, attempt, forever, loop, fromResult] imports [Effect, InternalTask] -Task ok err fx : InternalTask.Task ok err fx +Task ok err : InternalTask.Task ok err -forever : Task val err fx -> Task * err fx +forever : Task val err -> Task * err forever = \task -> looper = \{} -> task @@ -18,7 +18,7 @@ forever = \task -> Effect.loop {} looper |> InternalTask.fromEffect -loop : state, (state -> Task [Step state, Done done] err fx) -> Task done err fx +loop : state, (state -> Task [Step state, Done done] err) -> Task done err loop = \state, step -> looper = \current -> step current @@ -33,13 +33,13 @@ loop = \state, step -> Effect.loop state looper |> InternalTask.fromEffect -succeed : ok -> Task ok * * +succeed : ok -> Task ok * succeed = \ok -> InternalTask.succeed ok -fail : err -> Task * err * +fail : err -> Task * err fail = \err -> InternalTask.fail err -attempt : Task a b fx, (Result a b -> Task c d fx) -> Task c d fx +attempt : Task a b, (Result a b -> Task c d) -> Task c d attempt = \task, transform -> effect = Effect.after (InternalTask.toEffect task) @@ -50,7 +50,7 @@ attempt = \task, transform -> InternalTask.fromEffect effect -await : Task a err fx, (a -> Task b err fx) -> Task b err fx +await : Task a err, (a -> Task b err) -> Task b err await = \task, transform -> effect = Effect.after (InternalTask.toEffect task) @@ -61,7 +61,7 @@ await = \task, transform -> InternalTask.fromEffect effect -onFail : Task ok a fx, (a -> Task ok b fx) -> Task ok b fx +onFail : Task ok a, (a -> Task ok b) -> Task ok b onFail = \task, transform -> effect = Effect.after (InternalTask.toEffect task) @@ -72,7 +72,7 @@ onFail = \task, transform -> InternalTask.fromEffect effect -map : Task a err fx, (a -> b) -> Task b err fx +map : Task a err, (a -> b) -> Task b err map = \task, transform -> effect = Effect.after (InternalTask.toEffect task) @@ -83,7 +83,7 @@ map = \task, transform -> InternalTask.fromEffect effect -mapFail : Task ok a fx, (a -> b) -> Task ok b fx +mapFail : Task ok a, (a -> b) -> Task ok b mapFail = \task, transform -> effect = Effect.after (InternalTask.toEffect task) @@ -95,7 +95,7 @@ mapFail = \task, transform -> InternalTask.fromEffect effect ## Use a Result among other Tasks by converting it into a Task. -fromResult : Result ok err -> Task ok err * +fromResult : Result ok err -> Task ok err fromResult = \result -> when result is Ok ok -> succeed ok diff --git a/examples/cli/cli-platform/main.roc b/examples/cli/cli-platform/main.roc index 9877ce9ac5..8e185a6db1 100644 --- a/examples/cli/cli-platform/main.roc +++ b/examples/cli/cli-platform/main.roc @@ -1,9 +1,9 @@ platform "cli" - requires {} { main : InternalProgram } + requires {} { main : Task {} [] } exposes [] packages {} - imports [Effect.{ Effect }, InternalProgram.{ InternalProgram }] + imports [Task.{ Task }] provides [mainForHost] -mainForHost : Effect U8 as Fx -mainForHost = InternalProgram.toEffect main +mainForHost : Task {} [] as Fx +mainForHost = main diff --git a/examples/cli/cli-platform/src/lib.rs b/examples/cli/cli-platform/src/lib.rs index acb98743e3..eff562896d 100644 --- a/examples/cli/cli-platform/src/lib.rs +++ b/examples/cli/cli-platform/src/lib.rs @@ -213,7 +213,7 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut } #[no_mangle] -pub extern "C" fn rust_main() -> u8 { +pub extern "C" fn rust_main() { let size = unsafe { roc_main_size() } as usize; let layout = Layout::array::(size).unwrap(); @@ -223,11 +223,9 @@ pub extern "C" fn rust_main() -> u8 { roc_main(buffer); - let exit_code = call_the_closure(buffer); + call_the_closure(buffer); std::alloc::dealloc(buffer, layout); - - exit_code } } @@ -287,6 +285,11 @@ pub extern "C" fn roc_fx_setCwd(roc_path: &RocList) -> RocResult<(), ()> { } } +#[no_mangle] +pub extern "C" fn roc_fx_processExit(exit_code: u8) { + std::process::exit(exit_code as i32); +} + #[no_mangle] pub extern "C" fn roc_fx_exePath(_roc_str: &RocStr) -> RocResult, ()> { match std::env::current_exe() { diff --git a/examples/cli/cli-platform/src/main.rs b/examples/cli/cli-platform/src/main.rs index 0765384f29..57692d3619 100644 --- a/examples/cli/cli-platform/src/main.rs +++ b/examples/cli/cli-platform/src/main.rs @@ -1,3 +1,3 @@ fn main() { - std::process::exit(host::rust_main() as _); + host::rust_main(); } diff --git a/examples/cli/countdown.roc b/examples/cli/countdown.roc index 473fdc18a8..710ee83486 100644 --- a/examples/cli/countdown.roc +++ b/examples/cli/countdown.roc @@ -3,7 +3,7 @@ app "countdown" imports [pf.Stdin, pf.Stdout, pf.Task.{ await, loop, succeed }] provides [main] to pf -main = \_args -> +main = _ <- await (Stdout.line "\nLet's count down from 10 together - all you have to do is press .") _ <- await Stdin.line loop 10 tick diff --git a/examples/cli/echo.roc b/examples/cli/echo.roc index 6066ea2bd7..e380d095bf 100644 --- a/examples/cli/echo.roc +++ b/examples/cli/echo.roc @@ -1,18 +1,14 @@ app "echo" packages { pf: "cli-platform/main.roc" } - imports [pf.Stdin, pf.Stdout, pf.Task.{ Task }, pf.Program.{ Program, ExitCode }] + imports [pf.Stdin, pf.Stdout, pf.Task.{ Task }] provides [main] to pf -main : Program -main = Program.noArgs mainTask - -mainTask : Task ExitCode [] [Read [Stdin], Write [Stdout]] -mainTask = +main : Task {} [] +main = _ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂") - Task.loop {} (\_ -> Task.map tick Step) - |> Program.exit 0 + Task.loop {} \_ -> Task.map tick Step -tick : Task.Task {} [] [Read [Stdin], Write [Stdout]] +tick : Task.Task {} [] tick = shout <- Task.await Stdin.line Stdout.line (echo shout) diff --git a/examples/cli/env.roc b/examples/cli/env.roc index c53f7d7b26..981c7f49c8 100644 --- a/examples/cli/env.roc +++ b/examples/cli/env.roc @@ -1,25 +1,30 @@ app "env" packages { pf: "cli-platform/main.roc" } - imports [pf.Stdout, pf.Env, pf.Task, pf.Program.{ Program }] + imports [pf.Stdout, pf.Stderr, pf.Env, pf.Task.{ Task }] provides [main] to pf -main : Program +main : Task {} [] main = - Env.decode "EDITOR" - |> Task.await (\editor -> Stdout.line "Your favorite editor is \(editor)!") - |> Task.await (\{} -> Env.decode "SHLVL") - |> Task.await - (\lvl -> - when lvl is - 1u8 -> Stdout.line "You're running this in a root shell!" - n -> - lvlStr = Num.toStr n + task = + Env.decode "EDITOR" + |> Task.await (\editor -> Stdout.line "Your favorite editor is \(editor)!") + |> Task.await (\{} -> Env.decode "SHLVL") + |> Task.await + (\lvl -> + when lvl is + 1u8 -> Stdout.line "You're running this in a root shell!" + n -> + lvlStr = Num.toStr n - Stdout.line "Your current shell level is \(lvlStr)!") - |> Task.await (\{} -> Env.decode "LETTERS") - |> Task.await - (\letters -> - joinedLetters = Str.joinWith letters " " + Stdout.line "Your current shell level is \(lvlStr)!") + |> Task.await \{} -> Env.decode "LETTERS" - Stdout.line "Your favorite letters are: \(joinedLetters)") - |> Program.quick + Task.attempt task \result -> + when result is + Ok letters -> + joinedLetters = Str.joinWith letters " " + + Stdout.line "Your favorite letters are: \(joinedLetters)" + + Err _ -> + Stderr.line "I couldn't find your favorite letters in the environment variables!" diff --git a/examples/cli/file.roc b/examples/cli/file.roc index 8c71317a32..e7803d4a6b 100644 --- a/examples/cli/file.roc +++ b/examples/cli/file.roc @@ -1,7 +1,7 @@ app "file-io" packages { pf: "cli-platform/main.roc" } imports [ - pf.Program.{ Program, ExitCode }, + pf.Process, pf.Stdout, pf.Stderr, pf.Task.{ Task }, @@ -12,11 +12,8 @@ app "file-io" ] provides [main] to pf -main : Program -main = Program.noArgs mainTask - -mainTask : Task ExitCode [] [Write [File, Stdout, Stderr], Read [File, Env]] -mainTask = +main : Task {} [] +main = path = Path.fromStr "out.txt" task = cwd <- Env.cwd |> Task.await @@ -34,10 +31,7 @@ mainTask = Task.attempt task \result -> when result is - Ok {} -> - Stdout.line "Successfully wrote a string to out.txt" - |> Program.exit 0 - + Ok {} -> Stdout.line "Successfully wrote a string to out.txt" Err err -> msg = when err is @@ -47,5 +41,5 @@ mainTask = FileReadErr _ _ -> "Error reading file" _ -> "Uh oh, there was an error!" - Stderr.line msg - |> Program.exit 1 + {} <- Stderr.line msg |> Task.await + Process.exit 1 diff --git a/examples/cli/form.roc b/examples/cli/form.roc index 5df943c514..9cd6e34817 100644 --- a/examples/cli/form.roc +++ b/examples/cli/form.roc @@ -1,16 +1,12 @@ app "form" packages { pf: "cli-platform/main.roc" } - imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }, pf.Program.{ Program, ExitCode }] + imports [pf.Stdin, pf.Stdout, pf.Task.{ await, Task }] provides [main] to pf -main : Program -main = Program.noArgs mainTask - -mainTask : Task ExitCode [] [Read [Stdin], Write [Stdout]] -mainTask = +main : Task {} [] +main = _ <- await (Stdout.line "What's your first name?") firstName <- await Stdin.line _ <- await (Stdout.line "What's your last name?") lastName <- await Stdin.line Stdout.line "Hi, \(firstName) \(lastName)! 👋" - |> Program.exit 0 diff --git a/examples/cli/http-get.roc b/examples/cli/http-get.roc index f6e5b4059f..0333aba08f 100644 --- a/examples/cli/http-get.roc +++ b/examples/cli/http-get.roc @@ -1,13 +1,10 @@ app "http-get" packages { pf: "cli-platform/main.roc" } - imports [pf.Http, pf.Task, pf.Stdin, pf.Stdout, pf.Program.{ Program, ExitCode }] + imports [pf.Http, pf.Task.{ Task }, pf.Stdin, pf.Stdout] provides [main] to pf -main : Program -main = Program.noArgs mainTask - -mainTask : Task.Task ExitCode [] [Read [Stdin], Write [Stdout], Network [Http]] -mainTask = +main : Task {} [] +main = _ <- Task.await (Stdout.line "Please enter a URL to fetch") url <- Task.await Stdin.line @@ -25,4 +22,3 @@ mainTask = |> Task.await Stdout.line output - |> Program.exit 0 diff --git a/examples/helloWorld.roc b/examples/helloWorld.roc index 08c5230d8d..7eef5d17a1 100644 --- a/examples/helloWorld.roc +++ b/examples/helloWorld.roc @@ -1,11 +1,7 @@ app "helloWorld" packages { pf: "cli/cli-platform/main.roc" } - imports [pf.Stdout, pf.Program.{ Program }] + imports [pf.Stdout] provides [main] to pf -main = Program.noArgs mainTask - -mainTask = +main = Stdout.line "Hello, World!" - |> Program.exit 0 -