Tom MacWright

2025@macwright.com

Monad annoyance

Monad is a big unnecessary word but maybe I just mean something more like 'chainable Promise-like thing', since I'm coming from TypeScript land. What I'm annoyed by is that for years I've had a situation like this:

With these functions:

async function getOrganization(name: string): Promise<Organization>
async function getMembers(orgId: string): Promise<Members[]>

I'll want to do something like 'get an organization and its members'. So in beautiful elegant point-free style I could write:

const list = getOrganization(name).then(org => getMembers(org.id));

But this produces only the members list. I don't want chains to always replace the previous chained 'thing', in many cases I want them to add to it. So I'll end up doing:

const list = getOrganization(name).then(org => {
  return getMembers(org.id).then(members => {
    return {
      members,
      org
    }
  })
});

Much less beautiful, right? This happens all the time with Promise chaining as well as when I use neverthrow, and it's a little more cumbersome in the neverthrow case. I kind of want a 'combiningThen' method that would work like:

const [org, members] = getOrganization(name).combine(org => getMembers(org.id));

Even Effect, which is a swiss-army knife of every possible functional-programming concept, doesn't have something that seems obviously like this. Maybe chaining Effect.all would do it?

Having simple dependencies on earlier results is always the thing that makes elegant FP-flavored code look funky.