Skip to content

Instantly share code, notes, and snippets.

@ruxo
Last active April 28, 2023 23:21
Show Gist options
  • Select an option

  • Save ruxo/d60871dc265d12276b0ccccb479a6de3 to your computer and use it in GitHub Desktop.

Select an option

Save ruxo/d60871dc265d12276b0ccccb479a6de3 to your computer and use it in GitHub Desktop.
[package]
name = "io_monad_guessing_game"
version = "0.1.0"
authors = ["Ruxo Zheng <me@ruxoz.net>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1"
tinyrand = "0.5"
use anyhow::{Result, anyhow};
use std::{io::{stdin, stdout, Write}, time::SystemTime};
use tinyrand::{Rand, Seeded, StdRand};
trait IO<A> {
fn run(&mut self) -> Result<A>;
}
fn bind<A, B, F, R>(mut ma: impl IO<A>, f: F) -> impl IO<B>
where
R: IO<B>,
F: Fn(&A) -> R + 'static,
{
move || ma.run().and_then(|ref v| f(v).run())
}
fn bindr<A, B, F>(mut ma: impl IO<A>, f: F) -> impl IO<B>
where
F: Fn(&A) -> Result<B> + 'static,
{
move || ma.run().and_then(|ref v| f(v))
}
#[allow(dead_code)]
fn map<A, B, F>(mut ma: impl IO<A>, f: F) -> impl IO<B>
where
F: Fn(&A) -> B + 'static,
{
move || ma.run().map(|ref v| f(v))
}
fn retry<A>(mut ma: impl IO<A>) -> impl IO<A> {
move || {
loop {
match ma.run() {
v @ Ok(_) => return v,
Err(_) => ()
}
}
}
}
impl<A, F> IO<A> for F
where
F: FnMut() -> Result<A>,
{
fn run(&mut self) -> Result<A> {
(self)()
}
}
fn get_random(max: u32) -> impl IO<u32> {
move || {
let seed = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs();
let mut rand = StdRand::seed(seed);
Ok(rand.next_lim_u32(max))
}
}
fn read_line() -> impl IO<String> {
|| {
let mut s = String::new();
let len = stdin().read_line(&mut s)?;
s.truncate(len - 1);
Ok(s)
}
}
fn write_text(text: String) -> impl IO<()> {
move || {
stdout().write_all(text.as_bytes())?;
stdout().flush()?;
Ok(())
}
}
fn writeln_text(text: String) -> impl IO<()> {
move || {
stdout().write_all(text.as_bytes())?;
stdout().write(b"\r\n")?;
stdout().flush()?;
Ok(())
}
}
fn main() -> Result<()> {
let target = get_random(100);
let mut program = bind(target, |target| play(*target));
program.run()
}
fn play(target: u32) -> impl IO<()> {
let t = write_text("Guess: ".to_string());
let r = bind(t, |_| bindr(read_line(), |s| Ok(s.parse()?)));
let guessing = bind(r, move |guess| {
let guess: u32 = *guess;
let print_text = print_result(target, guess);
bind(print_text, move |_| get_guess_result(target, guess))
});
retry(guessing)
}
fn print_result(target: u32, guess: u32) -> impl IO<()> {
if guess < target { writeln_text("Too small".to_string()) }
else if guess > target { writeln_text("Too big".to_string()) }
else { writeln_text(format!("Congrat, it's {guess}!")) }
}
fn get_guess_result(target: u32, guess: u32) -> impl IO<()> {
move || {
if guess == target { Ok(()) }
else { Err(anyhow!("Guess incorrect")) }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment