Merge pull request #4582 from roc-lang/simplify-cli-platform

Drop Program and third type param from Task from CLI platform
This commit is contained in:
Richard Feldman 2022-11-24 17:57:07 -05:00 committed by GitHub
commit 2050f0ac2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 156 additions and 283 deletions

View File

@ -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 <ignored for test> ms."#
1 error and 0 warnings found in <ignored for test> ms."#
),
);
}

View File

@ -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 =
task =
_ <- await (line "a")
_ <- await (line "b")
_ <- await (line "c")
_ <- await (line "d")
_ <- await (File.readUtf8 (Path.fromStr "blah.txt"))
line "e"
# Type mismatch because this line is missing:
# |> Program.quick
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!"

View File

@ -7,3 +7,4 @@ tui
http-get
file-io
env
out.txt

View File

@ -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

View File

@ -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].

View File

@ -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]

View File

@ -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)

View File

@ -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)
## (`<60>`).
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)
## (`<60>`) 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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
## (`<60>`).
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)
## (`<60>`).
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

View File

@ -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 {})

View File

@ -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

View File

@ -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 {})

View File

@ -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

View File

@ -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

View File

@ -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::<u8>(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<u8>) -> 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<RocList<u8>, ()> {
match std::env::current_exe() {

View File

@ -1,3 +1,3 @@
fn main() {
std::process::exit(host::rust_main() as _);
host::rust_main();
}

View File

@ -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 <ENTER>.")
_ <- await Stdin.line
loop 10 tick

View File

@ -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)

View File

@ -1,10 +1,11 @@
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 =
task =
Env.decode "EDITOR"
|> Task.await (\editor -> Stdout.line "Your favorite editor is \(editor)!")
|> Task.await (\{} -> Env.decode "SHLVL")
@ -16,10 +17,14 @@ main =
lvlStr = Num.toStr n
Stdout.line "Your current shell level is \(lvlStr)!")
|> Task.await (\{} -> Env.decode "LETTERS")
|> Task.await
(\letters ->
|> Task.await \{} -> Env.decode "LETTERS"
Task.attempt task \result ->
when result is
Ok letters ->
joinedLetters = Str.joinWith letters " "
Stdout.line "Your favorite letters are: \(joinedLetters)")
|> Program.quick
Stdout.line "Your favorite letters are: \(joinedLetters)"
Err _ ->
Stderr.line "I couldn't find your favorite letters in the environment variables!"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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