Skip to content

Instantly share code, notes, and snippets.

@anurudhp
Last active June 23, 2021 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anurudhp/189d53c1c2c523bfd60ea6a8fef009ce to your computer and use it in GitHub Desktop.
Save anurudhp/189d53c1c2c523bfd60ea6a8fef009ce to your computer and use it in GitHub Desktop.
Simple IO Monad in C++
#include <functional>
#include <iostream>
#include <string>
using std::function;
using std::string;
// type IO a
template <class A>
struct IO {
function<A()> recipe;
IO(function<A()> r) : recipe(r) {}
A run() const { return recipe(); }
};
// instance Functor IO where
// fmap :: (a -> b) -> IO a -> IO b
template <typename A, typename B>
IO<B> fmap(function<B(A)> fn, IO<A> io_a) {
return IO<B>([=]() { return fn(io_a.run()); });
}
// instance Applicative IO where
// pure :: a -> IO a
template <typename A>
IO<A> pure(A val) {
return IO<A>([=]() { return val; });
}
// (<*>) :: IO (a -> b) -> IO a -> IO b
template <typename A, typename B>
IO<B> fappl(IO<function<B(A)>> io_a__b, IO<A> io_a) { // (<*>)
return IO<B>([=]() {
function<B(A)> f = io_a__b.run();
A a = io_a.run();
return f(a);
});
}
// instance Monad IO where
// return :: a -> IO a
// return = pure
#define Return pure
// bind :: IO a -> (a -> IO b) -> IO b
template <typename A, typename B>
IO<B> bind(IO<A> io_a, function<IO<B>(A)> fn) {
return IO<B>([=]() {
A a = io_a.run();
IO<B> io_b = fn(a);
return io_b.run();
});
}
// (>>=) = bind
template <typename A, typename B>
IO<B> operator>>=(IO<A> io_a, function<IO<B>(A)> fn) {
return bind(io_a, fn);
}
// seq :: IO a -> IO b -> IO b
template <typename A, typename B>
IO<B> seq(IO<A> io_a, IO<B> io_b) {
return IO<B>([=]() {
io_a.run();
return io_b.run();
});
}
// (>>) = seq
template <typename A, typename B>
IO<B> operator>>(IO<A> io_a, IO<B> io_b) {
return seq(io_a, io_b);
}
// type () = ()
struct Unit {
} unit;
// putChar :: Char -> IO ()
function<IO<Unit>(char)> putChar = [](char c) {
return IO<Unit>([=]() {
std::cout << c;
return unit;
});
};
// putStr :: String -> IO ()
function<IO<Unit>(string)> putStr = [](string s) {
return IO<Unit>([=]() {
std::cout << s;
return unit;
});
};
// putStrLn :: String -> IO ()
function<IO<Unit>(string)> putStrLn = [](string s) { return putStr(s + "\n"); };
// getChar :: IO Char
IO<char> getChar([]() {
char c;
std::cin >> c;
return c;
});
// getLine :: IO String
IO<string> getLine([]() {
string s;
getline(std::cin, s);
return s;
});
// main1 :: IO ()
// main1 = putStrLn "Hello World!"
IO<Unit> Main1 = putStrLn("Hello World!");
// main2 :: IO ()
// main2 = putStr "Name: " >>
// fmap (\s -> "Hello " ++ s ++ "!") getLine
// >>= putStrLn
IO<Unit> Main2 =
putStr("Name: ") >>
fmap<string, string>([](string s) -> string { return "Hello " + s + "!"; },
getLine) >>= putStrLn;
// main :: IO ()
IO<Unit> Main = Main1;
int main() { Main.run(); }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment