IOprelude
module IO
Console input and output.
IO is a capability: every function in it touches the outside world, so it can only be called from foul code (or from main). Reading a line and printing a line are the two workhorses; inspect is the debugging tool that can be dropped into the middle of a chain without changing its value.
main do
IO.print("name? ")
let name = IO.getLine.or("world")
IO.printLine("hello, ${name.trim}")
end
function printLine
Writes msg to stdout followed by a newline.
Any Showable value is accepted, not just strings: numbers, lists, maps and records print through their own show implementation. Called with no argument it prints an empty line.
printLine(msg) : Showable -> Void
Parameters
msgShowable- the value to write
Returns: Void —
Examples
Printing values of different types
IO.printLine("hello") # prints: hello
IO.printLine(42) # prints: 42
IO.printLine([1, 2, 3]) # prints: [1, 2, 3]
IO.printLine # prints an empty line
Interpolation is usually clearer than several arguments
let user = "ada"
IO.printLine("logged in as ${user}") # prints: logged in as ada
function print
Writes msg to stdout without a trailing newline.
Use it to build a line from several pieces, or to write a prompt that the cursor should stay on.
print(msg) : Showable -> Void
Parameters
msgShowable- the value to write
Returns: Void —
Examples
Building one line from several writes
IO.print("hello ")
IO.print("world")
IO.printLine # stdout: hello world
A prompt the answer is typed next to
IO.print("continue? [y/N] ")
let answer = IO.getLine.or("n")
function inspect
Writes a colored, structured rendering of val to stderr and returns val unchanged.
Because it returns its argument, inspect can be spliced into the middle of a chain to see what is flowing through it, then removed again without touching the surrounding code. It writes to stderr, so it does not disturb a program whose stdout is piped somewhere. The type checker treats it as pure, so it is allowed inside pure functions.
Use inspected instead when you want the rendering as a String rather than written out.
inspect(val) : A -> A
Parameters
valA- any inspectable value
Returns: A — the same value, unchanged
Examples
Watching an intermediate step of a chain
[1, 2, 3, 4]
.map { |n| n * 3 }
.inspect # stderr: [3, 6, 9, 12] : [Int]
.filter(~even?) # => [6, 12]
The rendering carries the value's type
IO.inspect("hi") # stderr: "hi" : String
IO.inspect((1..4)) # stderr: 1..4 : Range
function getLine
Reads one line from stdin, without the trailing newline.
Returns None at end of input, which is what makes it usable as a loop condition: the None is the end of the stream, not an error.
getLine() : String?
Returns: String? — the line, or None at end of input
Examples
Reading an answer with a default
let answer = IO.getLine.or("")
Draining stdin line by line
foul echoAll -> Void do
match IO.getLine do
Just(line) => do
IO.printLine(line)
echoAll
end
None => ()
end
end
function get
Reads a single character from stdin.
Returns None at end of input. Note that the result is a one-character String, not a Char.
get() : String?
Returns: String? — the character, or None at end of input
Examples
let key = IO.get.or("")
IO.printLine("you pressed ${key}")
function printError
Writes msg to stderr followed by a newline.
Diagnostics belong on stderr so that a program's real output can be piped or redirected on its own. Unlike a raised error, this only prints: it does not stop the program.
printError(msg) : Showable -> Void
Parameters
msgShowable- the message to write
Returns: Void —
Examples
Reporting a problem without exiting
IO.printError("config file not found: using defaults")
Keeping stdout clean for the real result
IO.printError("scanning ${dir}...") # progress, on stderr
IO.printLine(results.join("\n")) # the output, on stdout
function warn
Writes msg to stderr. Identical to printError, named for the case where the message is a warning rather than a failure.
warn(msg) : Showable -> Void
Parameters
msgShowable- the warning to write
Returns: Void —
Examples
IO.warn("ignoring unknown key ${key}")
function warning
Writes msg to stderr. The long spelling of warn.
warning(msg) : Showable -> Void
Parameters
msgShowable- the warning to write
Returns: Void —
Examples
IO.warning("this option is deprecated")
constant out FileHandle<CannotRead, CanWrite>
The three standard streams, as ordinary handle VALUES.
IO.printLine(x) and IO.out.printLine(x) are the same call: the convenience spelling stays, and the handle behind it is now something a program can name, pass and substitute. That is what Mock.IO cannot do: it is one global switch, so output from one library cannot go to a buffer while another's goes to the terminal, and a library cannot ACCEPT a sink (kexhq/kex#139).
Typestate says what each one permits: writing to IO.in, or reading from IO.out, is a compile error, exactly as it is for a file opened Read.
These three are PURE, so they are not part of the capability interface a stand-in must implement: naming a device performs no effect, writing THROUGH it does, and the handle methods are the foul ones. That also draws the seam between the two ways to redirect output: with IO = ... replaces the CALLS, so it does not touch a handle obtained here, while Mock.IO replaces the DEVICE (a group leader, kexhq/kex#141) and so captures IO.out.printLine(x) and IO.printLine(x) alike.
constant error FileHandle<CannotRead, CanWrite>
Standard error, as a handle. The sink IO.printError and IO.warn write to, reachable as a value.
constant in FileHandle<CanRead, CannotWrite>
Standard input, as a handle. The source IO.getLine and IO.get read from, reachable as a value.