enso: Init repo

This commit is contained in:
buckwheat
2025-12-25 19:21:16 -08:00
commit fdba0b01d4
7 changed files with 151 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# Files
*.org

16
LICENSE Normal file
View File

@ -0,0 +1,16 @@
THE MUKYU PUBLIC LICENSE
Draft 1, November 2010
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
THE MUKYU PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
1. This license document permits you to DO WHAT THE FUCK YOU WANT TO
as long as you AGREE TO NOT STEAL BOOKS FROM PATCHOULI'S LIBRARY.
This program is distributed in the hope that it will be magical,
but WITHOUT ANY WARRANTY; without even the implied warranty of
USEFULNESS or FITNESS FOR A PARTICULAR PURPOSE.

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# Enso - The Minimalist IO Monad Library
Enso is a minimal IO Monad library that provides only absolute basics, nothing more and nothing less. It's not very robust, and that's by design. It's not amazing, and that's also by design. Enso is both something I wrote to learn how Monads (such as IO Monads) work in hopes to respark my interest in Functional Programming, as well as a utility built to make the development of my projects heavily utilizing the paradigm much easier.
As a result, Enso is not exactly fantastical and may not be for you, unless you don't mind the batteries not included mentality of Enso. Furthermore, I am not amazing at Functional Programming, and I have learned all of what I know about it through Haskell. As well, I'm not an academic of any sort (not a Computer Sciences major nor student) and thus this most likely is not the most impressive attempt at an IO Monad. I'm more of the engineer praxis type, and I just wanted to see whether I can even do the task.
# 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.
# Dependencies
Enso doesn't depend on anything but the Nim language itself. As such, Enso is meant to be a batteries not included library that provides niceties to make Functional Programming a little quicker, or at least I tell myself that as I really just designed this out of sheer curiosity.
# Why should I use Enso?
The short answer, you shouldn't unless you are insane like I am.
The long answer is that maybe you're a lot like me and want to better understand how Functional Programming works under the hood for some reason, and you want to tinker around with building the systems yourself too. I also built Enso because the only Monad library I found for Nim is practically abandoned, and I was psychotic enough to start a project with an associate where we decided to use Functional Programming for it, but we had no good systems to use for IO that added a few bells and whistles for us. For all I know, Enso may get new code should we need more. I don't quite know yet.
If you like Enso, great. If you don't like Enso, great.

12
enso.nimble Normal file
View File

@ -0,0 +1,12 @@
# Package
version = "0.1.0"
author = "buckwheat"
description = "A minimalist IO Monad library in Nim"
license = "MIT"
srcDir = "src"
# Dependencies
requires "nim >= 2.0.4"

59
src/enso.nim Normal file
View File

@ -0,0 +1,59 @@
import std/sugar
# Declarations
type FileDesc* = enum STDIN, STDOUT, STDERR
type IO*[T] = proc(): T
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[seq[T]], fn: T -> U): IO[seq[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]
# Definitions
func flatmap*[T, U](io: IO[T], fn: T -> IO[U]): IO[U] = join(io.map(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] = io.map(run)
func map*[T, U](io: IO[seq[T]], fn: T -> U): IO[seq[U]] =
proc(): seq[U] =
let list: seq[T] = io()
if list.len == 0:
@[]
else:
let
head: T = list[0]
tail: seq[T] = list[1..^1]
new: IO[seq[T]] = to_IO tail
@[fn(head)] & run new.map(fn)
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

1
tests/config.nims Normal file
View File

@ -0,0 +1 @@
switch("path", "$projectDir/../src")

34
tests/test.nim Normal file
View File

@ -0,0 +1,34 @@
import enso
import std/strformat
proc unsafe_void(str: string) =
echo str
proc unsafe_int(str: string): int =
echo str
result = 1
func add_two(x: int): int = x + 2
func main() =
run strput("Hello from Enso!")
let
out_void: IO[void] = lift do(): unsafe_void("void")
out_int: IO[int] = lift proc(): int = unsafe_int("int")
out_void()
run strput(fmt"{out_int()}")
let
num_list: IO[seq[int]] = to_IO @[1, 2, 3, 4, 5]
list_added: IO[seq[int]] = num_list.map(add_two)
run strput(fmt"{list_added()}")
run strput("Enter a string")
let read: IO[string] = readstr(STDIN)
run strput(read())
when isMainModule:
main()