Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- [Effect-Oriented Programming](./effect-oriented-programming.md)
- [Library Effects](./library-effects.md)
- [Assert](./assert.md)
- [Console](./console.md)
- [Http and Https](./http-and-https.md)
- [Process](./process.md)
- [Modules](./modules.md)
Expand Down
105 changes: 105 additions & 0 deletions src/console.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Console

Flix provides `Console` as a library effect for terminal I/O. The `Console`
effect has a default handler, so no explicit `runWithIO` call is needed in
`main`. The key module is `Sys.Console`.

## The Console Effect

The `Console` effect supports reading from standard input and writing to
standard output and standard error:

```flix
pub eff Console {
/// Reads a single line from the console.
def readln(): String

/// Prints the given string `s` to the standard out.
def print(s: String): Unit

/// Prints the given string `s` to the standard err.
def eprint(s: String): Unit

/// Prints the given string `s` to the standard out followed by a new line.
def println(s: String): Unit

/// Prints the given string `s` to the standard err followed by a new line.
def eprintln(s: String): Unit
}
```

## The Console Module

The `Console` module provides several higher-level functions built on the
`Console` effect:

```flix
mod Sys.Console {
/// Prints prompt `p`, reads a line, and returns `default` if the input is empty.
def readlnWithDefault(p: a, default: String): String \ Console

/// Prints prompt `p`, reads a line, and applies `f` to the input.
/// Re-prompts on `Err(msg)`, returns `v` on `Ok(v)`.
def readlnWith(p: a, f: String -> Result[String, b]): b \ Console

/// Prints prompt `p` with a yes/no hint and reads a boolean answer.
/// Empty or unrecognized input returns `default`.
def confirm(p: a, default: {default = Bool}): Bool \ Console

/// Prints prompt `p` with a numbered list of choices and reads a selection.
/// Returns `None` if the input is invalid.
def pick(p: a, choices: List[b]): Option[b] \ Console

/// Like `pick`, but re-prompts until the user makes a valid selection.
def pickWith(p: a, choices: List[b]): b \ Console
}
```

## Basic Console I/O

The simplest use of `Console` is to print a prompt, read input, and respond:

```flix
use Sys.Console

def main(): Unit \ Console =
Console.print("What is your name? ");
let name = Console.readln();
Console.println("Hello ${name}!")
```

## Confirmed Input

The `Console.confirm` function asks a yes/no question and returns a `Bool`. You
can supply a default value that is used when the user presses Enter without
typing anything:

```flix
use Sys.Console

def main(): Unit \ Console =
let proceed = Console.confirm("Deploy to production?", default = true);
if (proceed)
Console.println("Deploying...")
else
Console.println("Aborted.")
```

## Validated Input

The `Console.readlnWith` function repeatedly prompts the user until the input
passes a validator. The validator returns `Ok(value)` on success or
`Err(message)` to re-prompt:

```flix
use Sys.Console

def main(): Unit \ Console =
let n = Console.readlnWith("Enter a number (1-10): ", s ->
match Int32.fromString(s) {
case Some(i) if i >= 1 and i <= 10 => Ok(i)
case _ => Err("Please enter a number between 1 and 10.")
}
);
Console.println("You entered: ${n}")
```
155 changes: 0 additions & 155 deletions src/library-effects.md
Original file line number Diff line number Diff line change
@@ -1,157 +1,2 @@
# Library Effects

The Flix Standard Library comes with a collection of algebraic effects and
handlers.

## Console

Flix defines a `Console` effect to read from and write to shell:

```flix
eff Console {
/// Reads a single line from the console.
def readln(): String

/// Prints the given string `s` to the standard out.
def print(s: String): Unit

/// Prints the given string `s` to the standard err.
def eprint(s: String): Unit

/// Prints the given string `s` to the standard out followed by a new line.
def println(s: String): Unit

/// Prints the given string `s` to the standard err followed by a new line.
def eprintln(s: String): Unit
}
```

### Example: Using `Console`

```flix
def main(): Unit \ IO =
run {
Console.println("Please enter your name: ");
let name = Console.readln();
Console.println("Hello ${name}")
} with Console.runWithIO
```

## FileReadWithResult

Flix defines a `FileReadWithResult` effect to read from the file system:

```flix
eff FileReadWithResult {
/// Returns `true` if the given file `f` exists.
def exists(f: String): Result[IoError, Bool]

/// Returns `true` is the given file `f` is a directory.
def isDirectory(f: String): Result[IoError, Bool]

/// Returns `true` if the given file `f` is a regular file.
def isRegularFile(f: String): Result[IoError, Bool]

/// Returns `true` if the given file `f` is readable.
def isReadable(f: String): Result[IoError, Bool]

/// Returns `true` if the given file `f` is a symbolic link.
def isSymbolicLink(f: String): Result[IoError, Bool]

/// Returns `true` if the given file `f` is writable.
def isWritable(f: String): Result[IoError, Bool]

/// Returns `true` if the given file `f` is executable.
def isExecutable(f: String): Result[IoError, Bool]

/// Returns the last access time of the given file `f` in milliseconds since the epoch.
def accessTime(f: String): Result[IoError, Int64]

/// Returns the creation time of the given file `f` in milliseconds since the epoch.
def creationTime(f: String): Result[IoError, Int64]

/// Returns the last-modified timestamp of the given file `f` in milliseconds since the epoch.
def modificationTime(f: String): Result[IoError, Int64]

/// Returns the size of the given file `f` in bytes.
def size(f: String): Result[IoError, Int64]

/// Returns a string of all lines in the given file `f`.
def read(f: String): Result[IoError, String]

/// Returns a list of all lines in the given file `f`.
def readLines(f: String): Result[IoError, List[String]]

/// Returns a vector of all the bytes in the given file `f`.
def readBytes(f: String): Result[IoError, Vector[Int8]]

/// Returns a list with the names of all files and directories in the given directory `d`.
def list(f: String): Result[IoError, List[String]]
}
```

### Example: Using `FileReadWithResult`

```flix
def main(): Unit \ IO =
run {
match FileReadWithResult.readLines("Main.flix") {
case Result.Ok(lines) =>
lines |> List.forEach(println)
case Result.Err(err) =>
println("Unable to read file. Error: ${err}")
}
} with FileReadWithResult.runWithIO
```

## FileWriteWithResult

Flix defines a `FileWriteWithResult` effect to write to the file system:

```flix
eff FileWriteWithResult {
/// Writes `str` to the given file `f`.
def write(data: {str = String}, f: String): Result[IoError, Unit]

/// Writes `lines` to the given file `f`.
def writeLines(data: {lines = List[String]}, f: String): Result[IoError, Unit]

/// Writes `data` to the given file `f`.
def writeBytes(data: Vector[Int8], f: String): Result[IoError, Unit]

/// Appends `str` to the given file `f`.
def append(data: {str = String}, f: String): Result[IoError, Unit]

/// Appends `lines` to the given file `f`.
def appendLines(data: {lines = List[String]}, f: String): Result[IoError, Unit]

/// Appends `data` to the given file `f`.
def appendBytes(data: Vector[Int8], f: String): Result[IoError, Unit]

/// Truncates the given file `f`.
def truncate(f: String): Result[IoError, Unit]

/// Creates the directory `d`.
def mkDir(d: String): Result[IoError, Unit]

/// Creates the directory `d` and all its parent directories.
def mkDirs(d: String): Result[IoError, Unit]

/// Creates a new temporary directory with the given prefix.
def mkTempDir(prefix: String): Result[IoError, String]
}
```

### Example: Using `FileWriteWithResult`

```flix
def main(): Unit \ IO =
run {
let data = List#{"Hello", "World"};
match FileWriteWithResult.writeLines(lines = data, "data.txt"){
case Result.Ok(_) => ()
case Result.Err(err) =>
println("Unable to write file. Error: ${err}")
}
} with FileWriteWithResult.runWithIO
```