The use-case presented is to "flatten nested code caused by using monadic data types". The example solution presented is very similar to other functional languages, but I wonder if there's alternative syntax we could explore that might be more friendly for those who are less familiar with the scary "monad" word :).
For example, what if you could put an "..." inside any location where a block body is expected, and that means "make the rest of this outer block be the contents of this block? That's confusing to say, so perhaps a few examples makes it clearer.
// How we could write it today
function processData(data) {
const result = [];
if (data !== undefined) {
for (const entry of data) {
for (const subEntry of entry) {
data.push(subEntry.map(value => {
return value + 1;
}));
}
}
}
return result;
}
// How we could write it with the `...` syntax
function processData(data) {
const result = [];
if (data !== undefined) {
for (const entry of data) { ... }
for (const subEntry of entry) { ... }
data.push(subEntry.map(value => { ... }));
return value + 1;
}
return result;
}
// Other examples from the O.P. showing how to solve the monadic use-case
const foo = monadic (x) => {
[x * 1, x * 2, x * 3].flatMap(y => { ... });
[y + 1, y + 2, y + 3].flatMap(x => { ... });
return z;
};
const fetchJSON = (url) => {
fetch(url).then((response) => { ... });
const { ok } = response;
if (!ok) return { ok, response };
return response.json().then((data) => { ... });
return { ok, data };
};
Notice how we can solve the same use-case, and more, with a syntax like this, and it's much easier for beginners to pick up and understand as well.
Now, this specific syntax has some problems of it's own that we could pick at. The main one I see is: I find it really difficult to track what variables are available to me - now I'm not just looking for const
/let
declarations, I also have to pay attention to whatever variables that got introduced in the middle of an expression, that were exposed to a { ... }
block, like what happens with x => { ... }
.
With some refinement, there may be ways to fix this issue. For example, maybe a line that uses the special { ... }
block has to also put something at the beginning of the line to visually mark the block, to help train our eyes to remember that something special is happening on that line, perhaps like this:
const fetchJSON = (url) => {
> fetch(url).then((response) => { ... });
const { ok } = response;
if (!ok) return { ok, response };
> return response.json().then((data) => { ... });
return { ok, data };
};
(that specific syntax example will, I'm sure, be a major ASI issue, but I'm ignoring that for now)
Or maybe you specify which variables you want to be added to the scope in that marker, like this:
const fetchJSON = (url) => {
const response from fetch(url).then((response) => { ... })
const { ok } = response;
if (!ok) return { ok, response };
const data from return response.json().then((data) => { ... });
return { ok, data };
};
Dunno, something to play with. The point is that, even though this classic monad-style syntax being presented is very common among functional languages, I'm not actually sure that that's the best way to solve the problem for a not-as-functional-language where many users aren't as familiar with the concept of a monad.