const std = @import("std");

// Monad.zig is attached at the bottom of this blog post,
// and includes a working implementation of everything
// this article covers
const Monad = @import("monad.zig").Monad;

/// Generate a random number
fn random(io: std.Io) Monad(u32) {
    var result: u32 = undefined;
    io.randomSecure(@ptrCast(&result)) catch unreachable;
    return Monad(u32).@"return"(result);
}

/// Print a number
fn print(value: u32) Monad(void) {
    std.debug.print("{x:08}\n", .{value});
    return .@"return"({});
}

fn randPrint(io: std.Io) Monad(void) {
    return Monad(std.Io)
        .@"return"(io)
        .bind(random, io)
        .bind(print, io);
}

/// main() uses a new member functions which
/// allow us to use Monads in a imperative context.
/// They are elaborated on below.
pub fn main(init: std.process.Init) !void {
    for (0..5) |_| {
        randPrint(init.io).yield(init.io);
    }
}
