import std/[options, os, sugar] # Declarations type IO*[T] = proc(): T Unit* = object func attempt*[T](uf: IO[T]): Option[T] func attempt*(uf: IO[void]): Option[Unit] func fileread*(file: string): IO[Option[string]] func filewrite*(data: string, file: string): IO[Option[Unit]] func flatmap*[T, U](io: IO[T], fn: T -> IO[U]): IO[U] func lift*(fn: proc(): void): IO[void] func lift*[T](fn: proc(): T): IO[T] func join*[T](io: IO[IO[T]]): IO[T] func map*[T, U](io: IO[T], fn: T -> U): IO[U] func readln*(fd: File): IO[string] func run*[T](io: IO[T]): T func strput*(str: string): IO[void] func to_IO*[T](val: T): IO[T] # Unsafe proc unsafe[T](uf: IO[T]): Option[T] = try: let sf: T = uf() some(sf) except: none(T) proc unsafe(uf: IO[void]): Option[Unit] = try: run uf some(Unit()) except: none(Unit) # Definitions func attempt*[T](uf: IO[T]): Option[T] = unsafe(uf) func attempt*(uf: IO[void]): Option[Unit] = unsafe(uf) func fileread*(file: string): IO[Option[string]] = proc(): Option[string] = if fileExists(file): attempt(lift () => readFile(file)) else: none(string) func filewrite*(data: string, file: string): IO[Option[Unit]] = proc(): Option[Unit] = attempt(lift () => writeFile(file, data)) func flatmap*[T, U](io: IO[T], fn: T -> IO[U]): IO[U] = join(map(io, fn)) func lift*(fn: proc(): void): IO[void] = proc(): void = fn() func lift*[T](fn: proc(): T): IO[T] = proc(): T = fn() func join*[T](io: IO[IO[T]]): IO[T] = map(io, run) func map*[T, U](io: IO[T], fn: T -> U): IO[U] = proc(): U = fn(run(io)) func readln*(fd: File): IO[string] = proc(): string = readLine(fd) func run*[T](io: IO[T]): T = io() func strput*(str: string): IO[void] = proc(): void = echo str func to_IO*[T](val: T): IO[T] = proc(): T = val