(If you're unfamiliar with algebraic effects, here's a great article: Algebraic Effects for the Rest of Us)
This is a single-goal library that utilizes generators to add algebraic effects to javascript and typescript.
Add it to your project by using npm install algebraify
or yarn add algebraify
, in case you didn't know.
import algebra from "algebraify";
const getUser = algebra(function* getUser(_, id) {
const name = getNameOfUser(id) ?? (yield "name");
const age = getAgeOfUser(id) ?? (yield "age");
return `USER ${name}: ${age} years old`;
});
const userString = getUser(100)
.case("name", "John Smith")
.case("age", 18)
.do();
// userString will fallback to using the name john smith and the age 18 if those respective calls fail
// Just change to an async generator function
const getUser = algebra(async function* getUser(_, id) {
const name = await getNameOfUser(id) ?? (yield "name");
const age = await getAgeOfUser(id) ?? (yield "age");
return `USER ${name}: ${age} years old`;
});
// And then await the promise returned by do()
const userString = await getUser(100)
.case("name", "John Smith")
.case("age", 18)
.do();
import algebra from "algebraify";
const getUser = algebra(function* getUser(request, id: number) {
// Note the calls to request and subsequent calls to as
const name = getNameOfUser(id) ?? (yield* request("name").as<string>());
const age = getAgeOfUser(id) ?? (yield* request("age").as<number>());
return `USER ${name}: ${age} years old`;
});
const userString = getUser(100)
.case("name", "John Smith")
.case("age", 18)
.do();
// userString will have the type `USER: ${string}: ${number} years old`
// Async changes are identical in ts
The request parameter is a function that returns a narrowly typed iterator. You don't need to know the details of how it works to use it; yield* request("name").as<string>()
is basically the same as doing yield "name"
, but using some type magic to tell typescript to trust us on the return type.
I'm trying to find an ergonomic way to pass effects up the stack -- currently I don't have many ideas, and the ones I do have are vague. I also worry about coloring the stack a lot (What Color Is Your Function?). If you have any ideas, no matter how unsure you are of them, please feel free to make an issue for discussion or even send me a message on discord (@protowalker).
Please contribute! I've never made a library that I wanted other people to use before, and my experience with JS/TS is small compared to my programming career. I'd appreciate it a ton!