/// Do some silly console IO that touches what all I've made
fn main() {
prompt("How many apples do you have? ")
.bind(|apples| {
let apples = apples.unwrap();
prompt("How many oranges do you have? ")
.bind(move |oranges| Io::ret((apples.clone(), oranges.unwrap())))
})
.bind(|(apples, oranges)| {
Io::ret((
apples.parse::<u32>().unwrap(),
oranges.parse::<u32>().unwrap(),
))
})
.bind(|(apples, oranges)| {
put_str("You have ")
.bind(|_| print(apples + oranges))
.bind(|_| put_str_ln(" fruits!"))
});
}
/// A computation that, when performed, does some I/O before returning a value of type `T`.
struct Io<T>(T);
/// Monad properties
///
/// A lot based on https://stackoverflow.com/a/31892905
impl<T> Io<T> {
/// Do no I/O and return T
fn ret(t: T) -> Io<T> {
Io(t)
}
///
fn bind<S>(self, f: impl Fn(T) -> Io<S>) -> Io<S> {
f(self.0)
}
}
/// Write `s` to stdout
fn put_str(s: &str) -> Io<()> {
Io::ret(s).bind(|s| {
print!("{s}");
Io::ret(())
})
}
/// Write `s` and `'\n'` to stdout
fn put_str_ln(s: &str) -> Io<()> {
Io::ret(s).bind(|s| {
println!("{s}");
Io::ret(())
})
}
/// Read a single line from stdin
fn get_line() -> Io<std::io::Result<String>> {
Io::ret(()).bind(|()| {
let mut buffer = String::new();
Io::ret(std::io::stdin().read_line(&mut buffer).map(|_| buffer))
})
}
/// Stdout's fd
fn stdout() -> Io<std::io::Stdout> {
Io::ret(std::io::stdout())
}
/// Flush an I/O buffer
fn flush(mut buf: impl std::io::Write) -> Io<std::io::Result<()>> {
Io::ret(buf.flush())
}
/// Write a prompt to stdout, read a line from stdin, and return that line with whitespace trimmed from the ends.
fn prompt(ps1: &str) -> Io<std::io::Result<String>> {
put_str(ps1)
.bind(|_| stdout())
.bind(flush)
.bind(|_| get_line())
.bind(|line| Io::ret(line.map(|s| s.trim().into())))
}
/// Write a printable value to the console
fn print<T: std::fmt::Display>(t: T) -> Io<()> {
Io::ret(t).bind(|t| {
print!("{t}");
Io::ret(())
})
}