The current stdio interface looks something like this:
let mut b: [u8; 5];
stdin.read(b);
stdout.write(b"b = ", b, b"\n");
From a design point of view, this is a pretty simple, magical way of defining IO that relies heavily on the compiler to implement it properly. This design was always meant to be temporary and eventually replaced with something more robust and strongly typed.
After some reflection, I have come to the conclusion that for the sake of backwards compatibility, it would be better to modify these functions to work a bit differently. There will still be some magic for the time being, but for the most part this should be fairly straightforward. The idea with this new design is that it should enable people to write code that will be backwards compatible for the most part. That is, we want to guarantee that at some level it will not require large changes to upgrade this to newer versions of the compiler. I'm being careful with the wording here because there will be some flexibility with breakages, but nothing so severe that it largely impacts people using the compiler. Had the design stayed as it is right now, these problems would be much worse.
Though backwards compatibility and breakages are an important concern, I am okay with some (read: very little) breakage if the compiler can generate a useful error that provides a clear (and hopefully easy) path to upgrade.
New Design: Standard Input
The new design is based on Rust's Read trait. We're going to be using the Read::read_exact() interface.
Code like this:
let mut b: [u8; 5];
stdin.read(b);
Will become:
let mut b: [u8; 5];
stdin.read_exact(b);
In the future when references are implemented, this will further become:
let mut b: [u8; 5];
stdin.read_exact(&mut b);
Though that is a small breakage I am willing to accept.
While this API may look very similar to the old one, it has a couple of important differences:
- The old
read() accepted multiple/variadic arguments. This new version only accepts a single argument
- The old
read() accepted any type. This new version only accepts [u8; N]
- The old
read() would return false if the read on any of its arguments failed (giving you no information about what actually failed). This new version will panic if the read fails
- The read can fail if there are not enough bytes to read (EOF is reached before reading enough) or if something else more catastrophic happens. See
Read::read_exact() for more info..
- If you're not familiar with what a panic is, it's a Rust language concept that basically means the program quits with an error. We emulate that in Brainfuck by printing a message and entering an infinite loop.
- Once algebraic datatypes are implemented, this panic will be replaced by a
Result type which will generate a compiler error if it is ignored, thus making explicit error handling possible
- To panic, use the brainfuck feature described here
New Design: Standard Output
For stdout, the change is much simpler. We're just replacing stdout.write with stdout.print since that is closer to what the function does and easier to replace in the future.
TODO
The current stdio interface looks something like this:
From a design point of view, this is a pretty simple, magical way of defining IO that relies heavily on the compiler to implement it properly. This design was always meant to be temporary and eventually replaced with something more robust and strongly typed.
After some reflection, I have come to the conclusion that for the sake of backwards compatibility, it would be better to modify these functions to work a bit differently. There will still be some magic for the time being, but for the most part this should be fairly straightforward. The idea with this new design is that it should enable people to write code that will be backwards compatible for the most part. That is, we want to guarantee that at some level it will not require large changes to upgrade this to newer versions of the compiler. I'm being careful with the wording here because there will be some flexibility with breakages, but nothing so severe that it largely impacts people using the compiler. Had the design stayed as it is right now, these problems would be much worse.
Though backwards compatibility and breakages are an important concern, I am okay with some (read: very little) breakage if the compiler can generate a useful error that provides a clear (and hopefully easy) path to upgrade.
New Design: Standard Input
The new design is based on Rust's
Readtrait. We're going to be using theRead::read_exact()interface.Code like this:
Will become:
In the future when references are implemented, this will further become:
Though that is a small breakage I am willing to accept.
While this API may look very similar to the old one, it has a couple of important differences:
read()accepted multiple/variadic arguments. This new version only accepts a single argumentread()accepted any type. This new version only accepts[u8; N]read()would returnfalseif the read on any of its arguments failed (giving you no information about what actually failed). This new version will panic if the read failsRead::read_exact()for more info..Resulttype which will generate a compiler error if it is ignored, thus making explicit error handling possibleNew Design: Standard Output
For stdout, the change is much simpler. We're just replacing
stdout.writewithstdout.printsince that is closer to what the function does and easier to replace in the future.TODO
stdin.readstdout.writetostdout.printstdout.writelntostdout.println