import std/[options, os, sugar] # Declarations type Error* = enum OK, ERR FileDesc* = enum STDIN, STDOUT, STDERR IO*[T] = proc(): T func fileread*(file: string): IO[Option[string]] func filewrite*(data: string, file: string): IO[Error] 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 readstr*(stream: FileDesc): 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 read_unsafe(file: string): Option[string] = try: let data: string = readFile(file) some(data) except IOError: none(string) proc write_unsafe(data: string, file: string): Error = try: writeFile(file, data) OK except IOError: ERR # Definitions func fileread*(file: string): IO[Option[string]] = proc(): Option[string] = if fileExists(file): read_unsafe(file) else: none(string) func filewrite*(data: string, file: string): IO[Error] = proc(): Error = write_unsafe(data, file) 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 readstr*(stream: FileDesc): IO[string] = case stream: of STDIN: lift proc(): string = readLine(stdin) of STDOUT: lift proc(): string = readLine(stdout) of STDERR: lift proc(): string = readLine(stderr) 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