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

Set-builder inspired notation for updates #{ x | property = n } #44

Closed
ceigey opened this issue Aug 18, 2019 · 8 comments
Closed

Set-builder inspired notation for updates #{ x | property = n } #44

ceigey opened this issue Aug 18, 2019 · 8 comments

Comments

@ceigey
Copy link

ceigey commented Aug 18, 2019

Some discussion has been brought up about whether with is appropriate. So here's a (sub)proposal inspired by Mint and Set builder notation.

Note this issue has been edited to keep things relevant.

Summary

The following syntax is proposed as a variation on x with .prop = n:

  • #{ x | prop = n, sub.prop = m }

The pipe could alternatively be replaced with "with", which in this case would be further contextualised by the #{ } brackets.

Inspiration

Mint uses, or rather, will use a set-builder inspired notation for record updates. This set builder notation is quite well known throughout the world of computer science, and while not all JavaScript developers have compsci backgrounds, there is a lot of inertia there.

This is a proposed example of a record update in Mint:

user =
  {
    name = "Stuart",
    address = {
      location = "Freedonia"
    }
  }

{ user |
  name = "Bob",  
  address.location = "Super Silly Fun Land"
}

(as taken from the Mint documentation, 18th Aug 2019)

Proposed use in JavaScript

Here, I would like to consider the above adapted for this JavaScript proposal, as follows:

let user = #{
  name: 'Stuart',
  address: {
    location: 'Sylvania',
  }
};

let newerUser = #{ user | name = 'Rob', address.location = 'Rather Serious Land' };

Thus: #{ RECORD | ASSIGNMENT_1, ..., ASSIGNMENT_N }.

In the ASSIGNMENTS section of the notation, direct properties of RECORD are now treated as variables which can be updated with any assignment operator (e.g. =, ++, +=, etc). In addition, you can update nested properties too.

This requires the reuse of one reserved symbol, |, normally the bitwise-or orperator, and introduces a new context for the interpreter/compiler.

The use of | here appears unambiguous, as I'm not sure how a bitwise-or could even be used in this context; to my knowledge, you'd always have a SyntaxError attempting to write something like this.

However, the use of with is also unambiguous here too (then again, with is fairly unambiguous in the original proposal, but at least in this case you don't need to do with .name = 'Bob' where a leading . is introduced). This should perhaps be separated out into a separate issue, perhaps at a later stage.

Comparisons

Compared to the with syntax already proposed:

let newerUser = user with .name = 'Bob', .address.location = 'Rather Serious Land';

Compared to possible use of + and - for updates (based on an idea shared by @ljharb in #41, see here):

let newerUser = user + #{ name: 'Bob', address: { location: 'Rather Serious Land' } };

Note that here, any additional properties of address are not overwritten, only location is updated.

Pros and cons

Pros

  • Resembles set-builder notation more

Cons

  • javascript record with is perhaps easier to search for than javascript record long-sticky-thing
  • | is still ambiguous in search engine results, just like with
  • still perhaps should be split off into another proposal that could include { ... | ..... } too.
  • Doesn't look very JS-y.
@ceigey
Copy link
Author

ceigey commented Aug 18, 2019

Splitting this out into a comment since it opens the scope up significantly, but there are even further variations on this idea, like:

let migratedUser = #{ ...user, ...userSettings, admin: false | user => {
  user.verification.email = false;
  user.verification.phone = false;
  user.profile.visibility = 'private';
}};

... where verification is automatically added as a property if it doesn't exist, such that:

let user = #{ name: 'Jane' };
let userSettings = #{ profile: { visibility: 'public', darkMode: false } };

let migratedUser = #{ ...user, ...userSettings, admin: 'false' | user => {
  user.verification.email = false;
  user.verification.phone = false;
  user.profile.visibility = 'private';
  if (user.profile.darkMode) {
    console.log('User is probably a programmer');
  }
}};

assert(migratedUser === #{
  name: 'Jane',
  profile: {
    visibility: 'private',
    darkMode: false
  },
  admin: false,
  verification: {
    email: false,
    phone: false,
  }
});

@dead-claudia
Copy link
Contributor

dead-claudia commented Aug 18, 2019

I've been reluctant to suggest this personally because of one main question: how would you spec out things like x with .y += 1 in this?

Edit: I mean specifically with a #{x | key: value} syntax. Also, #{x | key = value} doesn't look very JS-like IMHO.

@ceigey
Copy link
Author

ceigey commented Aug 18, 2019

My thoughts were just #{ x | y += 1 }. Since it's conceptually y = y + 1.

But your point about it looking a little un-JS makes me wonder if then perhaps a more Immer-like syntax would be better: #{ x with x => { x.y += 1 }}.

(While the first syntax (that doesn't accept functions) can just be desugared into a series of spreads (though preferably there'd be some sort of black-boxy optimisation going on), the second syntax implies that some sort of intermediate mutable object (with otherwise identical rules to the record) is made, passed to the function, then returned and compared for differences against the record.)

@mheiber
Copy link
Contributor

mheiber commented Aug 20, 2019

@ceigey would you be OK with the following, as you're suggesting two orthogonal changes?

  • update the issue to be only about the set-builder notation
  • open a separate issue re Immer-style semantics or save it for a follow-on proposal:
    • We have a better shot of getting the feature in if we can separate out follow-ons. I don't see anything in the current semantics that conflicts with Immerification
    • The Immer-style semantics could conflict with our main goal (perf)

@ceigey
Copy link
Author

ceigey commented Aug 25, 2019

Hi @mheiber, happy to oblige; I'll remove the Immer related bits and make this as simple as possible.

@mheiber
Copy link
Contributor

mheiber commented Aug 25, 2019

Thanks for your help!

@littledan
Copy link
Member

I think this will be best to discuss in a follow-on proposal. Let's add something in the README explicitly pointing to this possibility.

@rickbutton
Copy link
Member

I've added a section to the proposal that references this as a possibility for a follow-up proposal. Thank you for this!

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

5 participants