README.md | enso.nim: attempt added for safer Exception handling, README updated to reflect current state and philosophy

This commit is contained in:
buckwheat
2025-12-28 22:31:13 -08:00
parent 8e22fcc169
commit d568c49368
3 changed files with 28 additions and 18 deletions

View File

@ -9,7 +9,11 @@ As a result, Enso is not exactly fantastical and may not be for you, unless you
# Usage
Enso provides the IO Thunk in order to handle data and functions. Functions and any data made pure via `to_IO` are lazily evaluated and then able to be used on the spot or when necessary, with the `run` function intended to sugar the usage of functions stored this way. `flatmap`, `map`, `join`, and `lift` are also provided in order to be used with the IO Thunk or with any other potentially impure functions to be executed in the Monad's context. It's mostly bare bones other than that besides `readstr` to read from a UNIX file descriptor and `strput` to write a string to STDOUT.
Enso provides the IO Thunk in order to handle data and functions. Functions and any data made pure via `to_IO` are lazily evaluated and then able to be used on the spot or when necessary, with the `run` function intended to sugar the usage of functions stored this way. `flatmap`, `map`, `join`, and `lift` are also provided in order to be used with the IO Thunk or with any other potentially impure functions to be executed in the Monad's context. It's mostly bare bones other than that besides `readln` to read from a file descriptor and `strput` to write a string to STDOUT.
As well, Enso provides `attempt` to help work with effectful procedures in Nim that have the capability to raise an Exception, `attempt` moves an IO Thunk with the capability to raise an Exception to be detonated within an unsafe procedure where the effect will happen, and passing off a Some or None to then be evaluated for handling any potential errors in a safer, much more graceful manner. `attempt` pairs fantastically with `lift` for removing any potential issues caused by effectful code.
It is important still as the programmer to understand that while many of Enso's provided utilities can allow you to perform nicer Functional Programming in Nim, it does not hold your hand the way a language like Haskell or OCaml will with the paradigm, and you have to hold yourself accountable to the paradigm. That is to say, how you use Enso will reflect who you are as a programmer and who you are when the compiler stops holding your hand. Enso also will teach the academics how to be engineers and the engineers how to be academics.
# Dependencies

View File

@ -3,11 +3,13 @@ import std/[options, os, sugar]
# Declarations
type
Error* = enum OK, ERR
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[Error]
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]
@ -20,31 +22,35 @@ func to_IO*[T](val: T): IO[T]
# Unsafe
proc read_unsafe(file: string): Option[string] =
proc unsafe[T](uf: IO[T]): Option[T] =
try:
let data: string = readFile(file)
some(data)
except IOError:
none(string)
let sf: T = uf()
some(sf)
except:
none(T)
proc write_unsafe(data: string, file: string): Error =
proc unsafe(uf: IO[void]): Option[Unit] =
try:
writeFile(file, data)
OK
except IOError:
ERR
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):
read_unsafe(file)
attempt(lift () => readFile(file))
else:
none(string)
func filewrite*(data: string, file: string): IO[Error] =
proc(): Error = write_unsafe(data, file)
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))

View File

@ -43,8 +43,8 @@ func main() =
else:
run strput("flat had no data")
let writer: Error = run filewrite("Hello world!\n", "test.txt")
if writer == OK:
let writer: IO[Option[Unit]] = filewrite("Hello world!\n", "test.txt")
if writer().isSome:
run strput("Successfully wrote test.txt!")
else:
run strput("Could not write test.txt!")