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

Update explainer to introduce ~() for partial application #49

Merged
merged 5 commits into from
Oct 14, 2021

Conversation

rbuckton
Copy link
Collaborator

This make the following changes to the proposal:

  • Adds a ~ prefix to denote a partially applied argument list (i.e., f~(?, x)).
  • Adds ?0 ordinal placeholders.
  • Adds a ... "rest of the arguments" placeholder.
  • Allows use of partial application with new.
  • Removes partial application with template literals and tagged templates.
  • Removes (or rather, comments out for the time being) references to the pipeline operator.

Fixes #48
Fixes #43
Fixes #5

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
@jonatjano
Copy link

I see no mention of what happens when you use the same ordinal placeholder multiple times

const surround = String~(?1, ?0, ?1);
surround("text", "__"); // "__text__" ?

The explainer doesn't say if it is forbidden or if the argument is applied twice as expected
(Or did I overlook it ?)

@tabatkins
Copy link

You overlooked it - check line 115.

@rbuckton
Copy link
Collaborator Author

const surround = String~(?1, ?0, ?1);
surround("text", "__"); // "__text__" ?

Although, the String constructor doesn't work that way. I think you would want "".concat(?1, ?0, ?1) instead.

README.md Show resolved Hide resolved
README.md Outdated
printArgs(); // prints: a, b, c
printArgs(1, 2, 3); // prints: 1, 2, 3

const swapAC = printArgs~(?2, ?, ?0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This really confuses me. Just reading this, my first thought is that the unbound ? should be equivalent to a ?3 because it's directly right of a ?2.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would make the introduction of an ordinal placeholder into a refactoring hazard:

// original code
const g = f(?, 1, ?);
// g is (roughly): (_0, _1) => f(_0, 1, _1); 

// Now I need to add a 3rd parameter to `g`, but pass it as the first parameter to `f`:
const g = f(?2, ?, 1, ?);
//          ^^ (inserted/changed)

// g is now (roughly): (_2, _0, _1) => f(_2, _0, 1, _1);
//                      ^^               ^^

If the numbering were to continue from 2, I would have to rewrite the entire expression:

const g = f(?2, ?0, 1, ?1);
//          ^^   ^      ^

// g is now (roughly): (_2, _0, _1) => f(_2, _0, 1, _1);
//                      ^^               ^^

Otherwise I would end up with something completely unexpected:

// original code
const g = f(?, 1, ?);
// g is (roughly): (_0, _1) => f(_0, 1, _1); 

// Now I need to add a 3rd parameter to `g`, but pass it as the first parameter to `f`:
const g = f(?2, ?, 1, ?);
//          ^^

// g is now (roughly): (_0, _1, _2, _3, _4) => f(_2, _3, 1, _4);
//                      ^^  ^^  ^^  ^^  ^^       ^^  ^^     ^^
//                      ~~  ~~ (ignored)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For your first comment example, do you mean:

-// g is now (roughly): (_2, _0, _1) => f(_2, _0, 1, _1);
+// g is now (roughly): (_0, _1, _2) => f(_2, _0, 1, _1);

Copy link
Member

@jridgewell jridgewell Oct 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, what makes the ? equivalent to _0 or _1? Do they use a completely separate counter? Could I have f~(?1, ?, ?), which would be similar to (_0, _1) => f(_1, _0, _1)?

I think it might be clearer to forbid mixing ordinal and non ordinal placeholder.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point, and my current explanation is also a refactoring hazard. In the explainer currently, any ordinal placeholder reserves a position that a normal placeholder would otherwise occupy:

f~(?, ?, ?) // implicitly f~(?0, ?1, ?2)
f~(?0, ?, ?) // implicitly f~(?0, ?1, ?2)
f~(?, ?0, ?) // implicitly f~(?1, ?0, ?2)

Its possibly better for normal placeholders to always just increment from 0, as this works better with refactoring:

// above examples
f~(?, ?, ?) // implicitly f~(?0, ?1, ?2)
f~(?0, ?, ?) // implicitly f~(?0, ?0, ?1)
f~(?, ?0, ?) // implicitly f~(?0, ?0, ?1)

// refactoring f~(?, ?, ?) by inserting an ordinal argument anywhere
// `^` - inserted
// `=` - same

   f~(?0, ?, ?, ?)     // f~(?0, ?0, ?1, ?2)
//    ^^  =======            ^^  ==========

   f~(?, ?1, ?, ?)     // f~(?0, ?1, ?1, ?2)
//    =  ^^  ====            ==  ^^  ======

   f~(?, ?, ?5, ?)     // f~(?0, ?1, ?5, ?2)
//    ====  ^^  =            ======  ^^  ==

We could ban mixing them, but as long as we choose a consistent numbering mechanism there's no reason to ban mixing them outright in the language. A linter could ban mixing them instead, leaving it open to override a lint rule in special circumstances.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the explainer to indicate that the implicit order of non-ordinal placeholder arguments is unaffected by ordinal placeholder arguments, and added an example for refactoring to explain why this is intended to avoid a refactoring hazard.

I think its too early to ban the combination outright, as minimizing the impact of any potential refactoring in user code seems like a valid reason to permit the combination.

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
@rbuckton
Copy link
Collaborator Author

Merging, thanks for the feedback!

|> add(7, ?)
|> clamp(0, 100, ?); // shallow stack, the pipe to `clamp` is the same frame as the pipe to `add`.
// accept a fixed argument list:
const numbers = ["1", "2", "3"].map(parseInt~(?, 10)); // [1, 2, 3]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this example. "One way to fix a problem you probably didn't know was plaguing you."

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made sure to add this example to my slides 😁

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