Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Skip mark #53

Open
artalar opened this issue May 21, 2022 · 7 comments
Open

Skip mark #53

artalar opened this issue May 21, 2022 · 7 comments

Comments

@artalar
Copy link

artalar commented May 21, 2022

Hi! This proposal describes an interesting pattern with a few features and few of them is missed in the readme.

Reference transparency

// NO
setTimeout(() => resolve(value), ms);
// YES
setTimeout(resolve, ms, value);

One of the benefits of this codestyle is referential transparency, which mean that you nave NO unexpected issues with "value" mutation (if it "let" or "var", or some property). "resolve" will be called with exactly the passed "value" data, not with "value" state in the call time.

Unfortunately, almost none other APIs follow this arguments pattern. For example, you couldn't do this: somePromise.then(cb, otherParam). Partial application solves it: somePromise.then(cb~(?, otherParam)).

Reduce of extra computations

hugeList.forEach(x => doSome(a.b.c.d, x))
// VS
hugeList.forEach(doSome~(a.b.c.d, ?))

Here we have a complex data access (a.b.c.d) which could take some performance impact. Inline caches from an engine could solve it, until it comes to some computation, like some.getFrom('wat', 'wot'). Commonly, a good practice is moving a computation to a variable above, but this extra work is not cosy and a developer could forget about it.

This proposal solve it perfectly and it awesome, as I think.

Syntax

But adding an extra syntax for this feature is not worth it, from my side.

In other side, the main problem for implementing it as a new prototype method is that we need a way to mark empty arguments (?). What options do we have?

I propose to use special symbol for that (and lets call new method tie)

UPD: the first version of the proposed symbol name was empty, but as discussed skip would be better.

hugeList.forEach(x => doSome(a.b.c.d, x))
// VS
hugeList.forEach(doSome~(a.b.c.d, ?))
// VS
hugeList.forEach(doSome.tie(a.b.c.d, Symbol.skip))

Lazy operations

But that's not all!

We could reuse the symbol to perform other lazy operations:

// two traverse
list.filter((v) => v % 2).map((v) => v * 2)

// one traverse
list.map((v) => v % 2 ? v * 2 : Symbol.skip)
// not lazy
const sumOfFirstFive = list.reduce((acc, v, i) => i < 5 ? acc + v : acc, 0)

// lazy
const sumOfFirstFive = list.reduce((acc, v, i) => i < 5 ? acc + v : Symbol.skip, 0)

ramda reduced

What do you think?

@Akiyamka
Copy link

I worry that amidst the criticism of the confusing undefined in js, we will consider a additinal void - "empty". I suppose to call this symbol in agreement with the meaning it brings - Symbol.skip

@artalar artalar changed the title Empty mark Skip mark Sep 2, 2022
@whiteand
Copy link

whiteand commented Sep 2, 2022

Good idea @artalar

@Arhey-
Copy link

Arhey- commented Jan 24, 2023

I have next sugar proposal for

hugeList.forEach(doSome.tie(a.b.c.d, Symbol.skip))

what about

hugeList.forEach(x -> doSome(a.b.c.d, x))

or

hugeList.forEach(x ==> doSome(a.b.c.d, x))

or

hugeList.forEach(x <=> doSome(a.b.c.d, x))

And as a bonus, we can rearrange Array.prototype.forEach's callback arguments

arr.forEach((e, i) -> doSome(e, a.b.c, i))

or even

arr.forEach((_, i) -> doSome(i, a.b.c.d))

which is not possible with a ? placeholder or a Symbol.skip

@Arhey-
Copy link

Arhey- commented Jan 24, 2023

Also we need something like Symbol.skipAll for rest and Symbol.skipFor(indexOfArgument)

Sorry for humour

@Arhey-
Copy link

Arhey- commented Jan 24, 2023

But seriously:

somePromise.then((cached => r => cb(r, cached))(otherParam))

hugeList.forEach((abcd => e => doSome(abcd, e))(a.b.c.d))

Or just move a computation to a variable above, as you already wrote. Explicit is better


list.map((v) => v % 2 ? v * 2 : Symbol.skip)

flatMap?

const sumOfFirstFive = list.reduce((acc, v, i) => i < 5 ? acc + v : Symbol.skip, 0)

list.reduce((acc, v, i) => i < 5 ? acc + v : acc, 0)

Or in last case it's like an imperative break? Symbol.break🤔

@bathos
Copy link

bathos commented Jan 24, 2023

You need dedicated syntax to do this without cleaving the existing ES value space into two new value spaces, the “ES values that can be used as arguments” value space and the “notional absence of such an argument value” value space. The partial application syntax should be compatible with arbitrary functions, so a first-class value acting as sentinel is the wrong level of abstraction; the functions are callable with any ES values as arguments, but the sentinel value ends up conditionally second-class depending on the application mechanism used. This becomes particularly problematic for anything higher-order that would sometimes need to analyze what would have been “pass-through” arguments to distinguish “real argument values” from the sentinel value. Broadly it would break reflection of function application —

I worry that amidst the criticism of the confusing undefined in js, we will consider a additinal void - "empty". I suppose to call this symbol in agreement with the meaning it brings - Symbol.skip

— as the name chosen doesn’t change that it’s effectively moving the “nulls treadmill” further along. The syntactic solution for “skipping” in the current proposal avoids the problems that would arise if a new “not-a-value” runtime value controlled the behavior.

@shaedrich
Copy link

shaedrich commented Nov 29, 2024

what about

hugeList.forEach(x <=> doSome(a.b.c.d, x))

This is known as the spaceship operator in PHP—so, this could be confusing

what about

hugeList.forEach(x -> doSome(a.b.c.d, x))

This resembles CoffeeScript's (arrow) functions and therefore, might be mistaken therefor.

what about

hugeList.forEach(x ==> doSome(a.b.c.d, x))

No usage for this one comes to my mind, however, while it can be seen as a benefit that it looks similar to usual arrow function calls, these two could be confused with each other, which might make it harder for beginners to learn the difference.

In #44, @pepkin88 talked about _ as the skip mark instead of ?, which I would prefer, however, since it is a valid variable name, this could potentially be a breaking change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants