Skip to content

An algebraic effects library for javascript and typescript using generators

Notifications You must be signed in to change notification settings

Protowalker/algebraify

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Algebraify npm GitHub issues

Algebraic Effects Are Here! (sort of)

(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.

Usage

Add it to your project by using npm install algebraify or yarn add algebraify, in case you didn't know.

Examples

Javascript

sync:

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

async:

// 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();

Typescript

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.

Future Plans

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).

Contributing

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!

About

An algebraic effects library for javascript and typescript using generators

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published