/// 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::().unwrap(), oranges.parse::().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); /// Monad properties /// /// A lot based on https://stackoverflow.com/a/31892905 impl Io { /// Do no I/O and return T fn ret(t: T) -> Io { Io(t) } /// fn bind(self, f: impl Fn(T) -> Io) -> Io { 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> { 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 { Io::ret(std::io::stdout()) } /// Flush an I/O buffer fn flush(mut buf: impl std::io::Write) -> Io> { 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> { 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: T) -> Io<()> { Io::ret(t).bind(|t| { print!("{t}"); Io::ret(()) }) }