Monad annoyance

8 points by raffomania


wheybags

I gotta admit, I dont get this. You’ve taken a simple pair of calls and made it super complicated. Just making two variables and assigning them is imo WAY more readable than any of the options presented here.

Like

const org = await getOrg(...);
const members = await getMembers(org.blah, ...);

What’s wrong with that?

gulbanana

in languages with syntactic support for monads, this works ok.

C# (fiddle)

using System;
using System.Linq;

int[][] arrays = [[0,1,2], [], [0,1]];
					
var sizePlusIndex = 
	from a in arrays
	let c = a.Length
	from n in a
	select n + c;

sizePlusIndex.ToList().ForEach(Console.WriteLine);

Haskell (playground):

import Control.Monad.Random

die :: RandomGen g => Rand g Int
die = getRandomR (1,6)

main = do
    roll1 <- evalRandIO $ die
    roll2 <- evalRandIO $ die
    print (roll1 + roll2)
Blintk

That double return thing is ugly, I can agree on that but I think you’re just choosing your syntax badly.

you can do it in one line and still stay under 120 characters even without the follow up .then line:

const result = getOrganization(name)
   .then(async org => ({ org, members: await getMembers(org.id) }));

or shoot, even like this:

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

But I would prefer making another function called getOrgAndMembers(name);. I think this is a taste thing.

jo3_l

As other comments have mentioned, given that the OP deals with Promises, using await and separate variables is the most natural way to go here. However, as an interesting tangent, I want to note that even when one is not using Promises, it is in fact still possible to have (somewhat cursed) monadic sugar syntax in JavaScript!

Concretely, suppose one wants to build an ergonomic JavaScript library offering a Result<T, U> monad. To express a computation that combines multiple results, the obvious approach is to provide combinators such as Result.map and so on, but if many results are used at once this leads to callback hell. The ideal solution is to provide some kind of syntax sugar that’s analogous to async/await for Promises, or do notation in Haskell, or the ? operator in Rust, whereby one can sequence multiple results in a linear fashion.

Using (two-way) generator functions, this is possible. Specifically, as the library author, we can implement a utility run that accepts a user-provided generator function, representing some kind of computation, such that yielding a Result short-circuits the computation in the error case and resumes with the success value otherwise.

const result = run(function* () {
  const organization = yield getOrganization();
  const members = yield getMembers(...);
});

Implementing run is a fun exercise. The idea of course generalizes beyond Result.

Effect actually implements this approach via Effect.gen, and so does neverthrow via safeTry. The catch is twofold. First, in practice, all the libraries will require you to write yield*, not yield, since TypeScript doesn’t infer types for yield expressions but the related operator yield* can be typed in the desired way. Second, since there is no generator arrow function shorthand, writing the rather wordy function* () {} to define a generator function is required. (See this TC39 proposal for generator arrow function notation).

To be clear, I don’t really think emulating do notation using generator function is a good idea, but it’s a fun use case for a lesser-known language feature.