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

Bikeshedding syntax #10

Open
js-choi opened this issue Oct 2, 2021 · 98 comments
Open

Bikeshedding syntax #10

js-choi opened this issue Oct 2, 2021 · 98 comments
Labels
bikeshedding Question about naming or something similar

Comments

@js-choi
Copy link
Collaborator

js-choi commented Oct 2, 2021

2022-03 plenary bikeshedding slides

Possible criteria

Syntactic clarity

Would human readers often have difficulty with determining the syntax’s grouping?

Conciseness

Is the syntax significantly improve conciseness over the status quo?

Natural word order

Is the syntax’s word order more natural (e.g., subject.verb(object)) than the status quo?

Confusability with other JS features

Is there a risk of beginners and other developers confusing the syntax with regular dot property access?

Confusability with other languages

Is there a risk of developers confusing the syntax with visually similar syntaxes from other languages – especially if they have different semantics?

Overlap with other JavaScript features

Does the syntax greatly overlap with other features of the language?
(Note: A finding from the January post-plenary overflow meeting says, “In general, some overlap is okay, but too much is bad; we have to decide this on a case-by-case basis.”)

List of candidates

Receiver-first style (loose unbracketed)

This style was originally called “bind-this”, but we dropped function binding from it in 2022-03, so we renamed the style to “receiver first”.

rec :> fn(arg0)
rec ~> fn(arg0)
rec !> fn(arg0)
rec -> fn(arg0) 
rec #> fn(arg0)
rec ~~ fn(arg0)

Receiver-first style (tight bracketed)

This style was originally called “bind-this”, but we dropped function binding from it in 2022-03, so we renamed the style to “receiver first”.

rec:>fn(arg0)
rec~>fn(arg0)
rec->fn(arg0)
rec::fn(arg0)
rec:.fn(arg0)
rec-.fn(arg0)

Receiver-first style (bracketed)

rec~[fn](arg0)
rec![fn](arg0)
rec#[fn](arg0)

Function-first style

This style was originally called “call-this”, but we are now calling it “function first” to distinguish it from receiver-first call-this. See the original explainer by @tabatkins.

fn@.(rec, arg0)

This-argument style

First proposed by @rbuckton.

fn(this: rec, arg0)

Original post

@rkirsling brought up in Matrix a few days ago the reasonable concern that -> may still be confusing to beginners with ..

I would be GENUINELY scared at making every beginner worry about "was it . that I'm supposed to write? but there's also ->..."

-> is a charged symbol. It has precedent as “method call” in Perl and PHP, but this proposal is for an operator that simply “changes the receiver of a function”, which is related but different.

I’m not a huge fan of ::, since that reads as “namespacing” to me, but I plan to tentatively switch back from -> to :: before the October plenary. There’s also ~> and ~~ as possibilities. I don’t have any better ideas for its spelling right now.

@js-choi js-choi added documentation Improvements or additions to documentation question Further information is requested bikeshedding Question about naming or something similar labels Oct 2, 2021
@bmeck
Copy link
Member

bmeck commented Oct 6, 2021

I have slight reservations about the right hand side of the operator being a dynamic variable lookup in a syntax that doesn't look like it is. In property access and method calling in particular in order to achieve a dynamic lookup you have to enclose the expression via [] like obj[method](). I don't think this is fatal, but a bit jarring to not have the expression with a wrapping set of tokens and instead matching . in only have a preceding token.

@ljharb
Copy link
Member

ljharb commented Oct 6, 2021

It is a bit atypical; it would be consistent, but unfortunate, if we were forced to only do obj->[method]().

@js-choi
Copy link
Collaborator Author

js-choi commented Oct 6, 2021

@bmeck: Yeah, we could use an always-circumfix binary operator, like …~[…] or …![…] or whatever.

…~[…] might be confusing with @rbuckton PFA syntax’s …~(…) (tc39/proposal-partial-application#48), but they can work together and they’re kind of analogous to one another…

obj~[fn](arg) // this-call

fn~(arg, ?) // PFA

obj~[fn]~(arg, ?) // PFA with a this-call 😄

@js-choi
Copy link
Collaborator Author

js-choi commented Oct 6, 2021

The more I think about …~[…], the more I like it.

I’m planning to switch the operator from …->… to …~[…] soon, barring any big objections.

@ljharb
Copy link
Member

ljharb commented Oct 6, 2021

I definitely think using ~ would be confusing given PFA’s change to it.

@js-choi maybe best not to so rapidly change the syntax of a proposal - i strongly prefer a dot/non-bracket mechanism, and i wouldn’t want ~ even if it’s a bracket mechanism and if PFA weren't using it, because it doesn’t convey any meaning about what it’s doing.

@js-choi
Copy link
Collaborator Author

js-choi commented Oct 6, 2021

Alright. We did get strong pushback about …->… from @rkirsling, though, so I worry about pushback about it from others at the plenary. At the very least, maybe we should switch to …::… before the plenary.

@ljharb
Copy link
Member

ljharb commented Oct 6, 2021

That concern seems like it would apply to any left-to-right syntax, including ::, and either way that’s worth discussing in plenary. I’m fine with switching back to :: in the meantime tho.

js-choi added a commit that referenced this issue Oct 6, 2021
@js-choi js-choi removed documentation Improvements or additions to documentation question Further information is requested labels Oct 6, 2021
@jridgewell
Copy link
Member

We could also embrace the pipe-ness and use :> or similar X> operator.

@rkirsling
Copy link
Member

That concern seems like it would apply to any left-to-right syntax, including ::, ...

For the committee as a whole that could be true, but it's actually the specific symbol -> that I view as charged with expectation—I just really don't want "wait, is it . or ->?" to ever be a dilemma JS newcomers have to face.

@ljharb
Copy link
Member

ljharb commented Oct 6, 2021

@rkirsling how is that different from ". or ::"? -> doesn't seem any more common an expectation to me than :: (it's used heavily in PHP, and less so in Ruby).

@rkirsling
Copy link
Member

Hmm, I mean :: makes me think of package/module access, so while it's true that in the past I saw a code example with :: and was unable to guess what we meant by it, it was clear that it was going to be a brand-new meaning.

Anyway, I'm not specifically advocating for ::; I just wanted to explain my apprehension about ->.

@ljharb
Copy link
Member

ljharb commented Oct 6, 2021

I'm confused about the apprehension, is all - are there specific languages that inform your expectation that it will be confusing?

@jridgewell
Copy link
Member

jridgewell commented Oct 6, 2021

C++ uses -> to mean property access of an object pointer:

// this pseudo code
Obj o = {};
o.foo; // regular access

Obj* p = &o;
p->foo; // same as `(*p).foo`, which is `o.foo`

@ljharb
Copy link
Member

ljharb commented Oct 6, 2021

I suspect if we made an extensive list, literally any viable infix option will probably be used as property access in some other language.

One question that guides my thinking here is: do we think more JS devs come from C++, or from PHP or Ruby?

@rkirsling
Copy link
Member

rkirsling commented Oct 6, 2021

Admittedly, if it were just C/C++ then perhaps that wouldn't suffice for a strong objection, but I'm thinking of my own experience of needing to reluctantly edit a Perl script—I really wasn't expecting to encounter -> vs. . in a scripting language and needed to figure out whether the implications were the same as C or not.

Are you suggesting that other choices would cause PHP/Ruby devs to worry about normal . usage? My concern is about two operators being viewed as a pair such that existing code becomes more confusing.

@ljharb
Copy link
Member

ljharb commented Oct 6, 2021

My understanding of your concern is, that based on expectations from C++ and Perl (and likely others), that a->b(c) may imply the semantics of a.b(c), which will cause confusion.

To a PHP user, a::b(c) already implies those semantics, and I do not believe that will cause confusion - they're different sigils, and the positioning doesn't necessarily imply "member access" to me.

It seems to me that while we may disagree on "it will be confusing", that the two are in the same identical bucket - iow, either they're both confusing, or neither is.

@rkirsling
Copy link
Member

At any rate, I'm fine with someone calling :: confusing, I just wasn't myself bringing that point to the table. 😅

@rbuckton
Copy link

rbuckton commented Oct 7, 2021

I suspect if we made an extensive list, literally any viable infix option will probably be used as property access in some other language.

One question that guides my thinking here is: do we think more JS devs come from C++, or from PHP or Ruby?

C# also uses -> for indirect property access through a pointer, and there's a fair amount of C# developers that also write JavaScript.

@acutmore
Copy link

Note that -> are => lambdas in languages like CoffeeScript, Elm, Java, Julia, Kotlin and LiveScript.

@js-choi js-choi changed the title Bikeshedding operator token Bikeshedding syntax Jan 27, 2022
@js-choi
Copy link
Collaborator Author

js-choi commented Jan 27, 2022

After today’s ad-hoc meeting on dataflow proposals, I have edited this issue to focus on general syntax bikeshedding. There are three possible styles.

  • fn(rec: arg0) or fn(this: rec, arg0): this-argument style, first proposed by @rbuckton.
  • fn@(rec, arg0): call-this style; see the original explainer by @tabatkins.
  • rec::fn(arg0): bind-this style. :: might be some other operator, like !., ->, or ![ ].

@bergus
Copy link

bergus commented Jan 27, 2022

I dislike the fn(rec: arg1) syntax. It looks more like a type declaration (which doesn't make sense in a call) or a named argument (which JS doesn't have). I do like the idea of a marker on the zeroth argument (a "this call" syntax, comparable to spread syntax), but it should still be separated by a normal comma from the rest of the arguments. Maybe fn(@rec, arg1, arg2) or using the this keyword fn(this: rec, arg1, arg2)/fn(this=rec, arg1, arg2)?

I do like both of the other syntaxes, we just should ensure that they don't collide with the extension methods, pipeline, or decorator proposals.

@ljharb
Copy link
Member

ljharb commented Jan 27, 2022

My preference of those three is definitely bind-this > call-this > this-arg; "this-arg" style feels very unjavascripty (it does feel slightly typescripty, which may be a pro to some but is a con to me)

If we can find an operator for bind-this style that doesn't evoke confusion with "dot access" for some delegates, I would be pleased.

@rkirsling
Copy link
Member

I like call-this quite a bit and don't dislike the explicit-this form of this-arg; I had expressed an undue amount of concern about :: in the meeting we had just now (having evidently forgotten the discussion above), but I think my opinion really boils down to: I'd prefer having the receiver be within the parens.

@varna
Copy link

varna commented Mar 30, 2022

:: is widely used in other languages too:

:: creates a member reference or a class reference. (kotlin)

Scope Resolution Operator (::) (php)

etc

The goal is to make this API more usable and approachable. All suggested operators seems quite cryptic or similar to something totally different. It took me 10 minutes to explain this to my junior and his response was "wtf is this bullsh*t"

Why not just call it call?

const add = (n) => this + n
let item = 0
item call add(3)
// 3

let self = item
self call toString()
// "0"

My junior never uses .call cause it is too obscure and hard to remember. Hell, even I rarely use it for same reasons. But with item call add(3) example it is so stupidly simple, that it's hard to not understand. It also seems quite readable with proper linting.

@ljharb
Copy link
Member

ljharb commented Mar 30, 2022

It would be pretty confusing to have fn.call(obj, arg) and obj call fn(arg) be the same thing.

@noppa
Copy link

noppa commented Mar 30, 2022

@js-choi 's comment above about the precedence and :> apply to identifier-like operators too.
If the natural (subjective) formatting or—even more importantly—the required formatting (objective) of the operator has spaces around it, it strongly implies loose precedence, like with the pipeline |>. I'd expect x call foo().bar() to mean (foo().bar).call(x) but the currently planned tight precedence rules would make it foo.call(x).bar().

@jridgewell
Copy link
Member

With the new keyword, new foo().bar() parses as (new foo()).bar()

@theScottyJam
Copy link
Contributor

theScottyJam commented Mar 30, 2022

Yeah, that one surprised me. Once I learned about it, I quickly got used to it, but it certainly doesn't look that's what would happen.

I would have expected behavior that's more similar to await

await foo().bar()
// the same as
await (foo().bar())

@js-choi
Copy link
Collaborator Author

js-choi commented Mar 30, 2022

With the new keyword, new foo().bar() parses as (new foo()).bar()

The readme (which assumes tight unbracketed receiver-first syntax) talks about a similar problem; it has some early errors to deal with new shenanigans:

A new expression may not contain a call-this expression without parentheses.
new x~>fn() is a SyntaxError.
Otherwise, new x~>fn()
would be visually ambiguous between
(new x)~>fn() and new (x~>fn()).

@theScottyJam
Copy link
Contributor

@js-choi - I believe the comment about new was mostly in response to @noppa's comment. @noppa mentioned that they would expect a keyword operator to have loose precedence, and @jridgewell countered by showing that new is a keyword operator, and it has a tight precedence.

@bathos
Copy link

bathos commented Mar 30, 2022

is a keyword operator, and it has a tight precedence.

when it feels like it :) (new foo.bar)

(no disagreement — just hadn’t appreciated till now how this could seem like one operator with “two precedences”.)

@theScottyJam
Copy link
Contributor

theScottyJam commented Mar 30, 2022

Perhaps @varna is onto something though. I know there's a high bar for introducing new syntax into JavaScript, and there's been some worry about the number of different proposals that are coming through that can be considered related to flow control.

Some syntaxes are more expensive than others. Some of the hesitation that comes with adding new syntax is:

  • We don't want to turn the language into ascii soup, where users are required to remember what all sorts of different ascii symbol combinations mean
  • There's limited ascii symbols to be used

If we can find a relatively concise and explanatory keyword form for this operator, perhaps this proposal would have a greater chance of success.

Here's some options that goes along this avenue:

// My favorite
// The function "receives" the receiver, because we're "sending" it.
user sendTo getName()

// Other explicit options
user receivedBy getName() // <-- I also like this one
user asThisFor getName()
user calledAgainstBy getName()

// Less verbose, generic options
user into getName()
user with getName()

If we go this route, I would argue that we go back to having a loose precedence, maybe something akin to instanceof (it would be nice if we tried to make keyword operators that generally had the same precedence as each other), though an argument could be made for giving it similar semantics to new precedence-wise, as both the sendTo and new operator would be trying to modify how a function gets called.

This option certainly has a number of upsides that the other options don't have. For example, this could perhaps resolve some of @rkirsling's concerns, without requiring us to use an operator that looks like a pipeline and yet behaves fairly differently from one.

It is a bit more verbose, which is certainly a downside. However, perhaps it's worth asking ourselves how important it really is to be concise on this matter. The general sentiment I'm getting from the README and conversations is that the primary issue with .call() is that it's not easy to read, a major concern being word-order. Just because something's used a ton doesn't necessarily mean we need ultra-concise syntax for it, I mean, think about how often new gets used (I'm sure it's more often than .call()), but I don't feel like people complain about the fact that it's a keyword instead of an operator, because it's easy to read and it feels natural to use. If JavaScript happened to have used create instead of new, I still don't think people would mind how verbose it is (they might dislike how it's different from other languages, but I doubt there would be many complaints around those extra couple of letters). sendTo is just as long as create, solves the word-order problem, and IMO feels a little more natural than some of the other options we're tossing around, all while not taking up much syntax space.

Anyways, just another option to toss around.

@js-choi
Copy link
Collaborator Author

js-choi commented Mar 31, 2022

I am fine with a word operator (as an unbracketed receiver-first syntax) if other representatives are okay with one, but I would want it to have tight precedence, and I would want it to have short length.

Let us not forget that .call is one of the most common methods in the language (thanks again @theScottyJam for your help on #12)—and that excessive boilerplate, not only word order, is also a factor in readability.

If only word order were the determinant of readability, then the pipe operator alone would improve .call’s readability, but input |> fn.call(@, arg0) is arguably even less readable than fn.call(input, arg0), and certainly less than input~>fn(arg0) or input:.fn(arg0) or whatever. Multiplying this by .call’s large frequency, it’s still worth considering whether a non-textual operator is worth the large potential improvement in code readability.

Having said that, maybe it’s possible to have a word operator that’s short enough to still be readable:

input on fn(arg0)
input to fn(arg0)

I wonder if there are any ASI hazards here, given that these aren’t keywords.

@ljharb
Copy link
Member

ljharb commented Mar 31, 2022

There is an ASI hazard with all of them, but we'd probably use a NLTH ("no line terminator here") to avoid that.

@js-choi
Copy link
Collaborator Author

js-choi commented Mar 31, 2022

One more thing with a text operator is that it might look strange when mixing them with optional chaining:

input.prop?.~>fn(arg0).prop
input.prop?.-.fn(arg0).prop
input.prop?.:fn(arg0).prop
input.prop?.:.fn(arg0).prop
input.prop?.::fn(arg0).prop
input.prop ?.on fn(arg0).prop
input.prop ?.to fn(arg0).prop

@bergus
Copy link

bergus commented Mar 31, 2022

Would "no line terminator here" work with multiline method chains though?

value
  .method()
  to callExtension()
  .otherMethod();

@ljharb
Copy link
Member

ljharb commented Mar 31, 2022

@bergus yes, it would likely be between the to and the callExtension that the line terminator is forbidden.

@theScottyJam
Copy link
Contributor

theScottyJam commented Mar 31, 2022

input.prop ?.to fn(arg0).prop

This actually looks fine to me. At least, compared to the awkwardness of these sorts of things:

x.fn?.(y)
x.value?.[y]

(I found those to be really gross when they first came out. I was used to CoffeeScript's style which just had a question mark without a dot before ( or [. I know why it needed to be done that way in JavaScript, but still, it's not pretty).

I find this one to be a bit worse - this is just a dot soup 😆

input.prop?.:.fn(arg0).prop

@bergus
Copy link

bergus commented Mar 31, 2022

@theScottyJam The dot soup should actually be input.prop.:fn(arg0).prop and input.prop?.:fn(arg0).prop for optional chaining - .: not :..

@bathos
Copy link

bathos commented Mar 31, 2022

Ergonomics is the primary reason for my interest in this proposal. I would like to be able to convert a large amount of code that currently uses “topicalized” functions (unshifted receiver). This would eliminate the need for these extra functions (of which there are thousands). Much of this code suffers from the oops-I-accidentally-the-whole-lisp parentheses/ordering/readability problem that pipelines could also address, but pipelines would not alleviate the need for creating those topicalized variants and in any case, :: would be more clear and direct than pipelines here.

I am biased towards :: in particular because I already know it solves this specific problem without the need for speculation. It was used where I worked during the stage-why-not? fervor of the early Babel era. I don’t recall it being a source of confusion for devs who weren’t “JS native speakers” there, either, which is why I personally haven’t been convinced that its various meanings in other languages present a problem. Symmetry with member access is more or less the point from my POV.

I’m sure there are other sigils that would get the job done with comparable clarity, but I’m describing a bit about my specific use case because I suspect many folks have different needs and so it may not be understood why some of us feel strongly about the need for concision and tight binding. Those things are the feature to me and I don’t think any of the alternatives I’ve seen actually solve my problems. If I were to naively convert the existing code in question to use ::, lines including 3 or more ::’s would not be uncommon. Naturally many of these expressions would benefit from being broken up across lines — which is one of the things :: helps with, and again it does so in a manner which for us would be more straightforward than pipelines because these functions are methods. So this operator would not be used once or twice here and there — it would be absolutely ubiquitous, occurring hundreds of times in a given module, and would be the “normal” function call in this context.

We cannot upgrade getName(user) to user calledAgainstBy getName(), say. That’s a non-starter; this would kill readability given its frequency and the need for chaining & use in argument position. Verbosity often does improve readability — not trying to golf here — but it only does so when it is not simultaneously devastating the signal-to-noise ratio. The value of :: for me was chiefly about getting that signal-to-noise ratio right.

@theScottyJam
Copy link
Contributor

theScottyJam commented Mar 31, 2022

Ergonomics is the primary reason for my interest in this proposal. I would like to be able to convert a large amount of code that currently uses “topicalized” functions (unshifted receiver). This would eliminate the need for these extra functions (of which there are thousands).

Could you expound on this use case a bit more, maybe with something a bit more concrete. I'm not quite grasping the high-level picture you're trying to paint here.

@bathos
Copy link

bathos commented Mar 31, 2022

Could you expound on this use case a bit more, maybe with something a bit more concrete. I'm not quite grasping the high-level picture you're trying to paint here.

It’s code where nearly all intrinsic or host-provisioned method and accessor functions that are used anywhere at all are first captured and mapped to new functions with (receiver, ...args) signature. This occurs in a context where the environment can (& must) be assumed to be in its “initial state” at evaluation time, but where it cannot be assumed that it will remain in its initial state at any point after evaluation.

// A. it is currently
slice(toLowerCase(normalize(string)), 1); // <-- “backwards” + the “lisp parentheses” problem for a long “chain”
// B. but could be
string::normalize()::toLowerCase()::slice(1);
// C. just like in code with “perpetual” environmental assumptions
string.normalize().toLowerCase().slice(1);
// D. or the proposal could become a divine punishment for daring to dream of `::`’s triumphant homecoming 🥲
string calledAgainstBy normalize() calledAgainstBy toLowerCase() calledAgainstBy slice(1);

In A, slice is a function like (receiver, ...args) => %Reflect.apply%(%String.prototype.slice%, receiver, args). In B, slice is %String.prototype.slice% itself. The intrinsic must still be initially captured, but there is no longer a need for an intermediate function.

@varna
Copy link

varna commented Apr 1, 2022

Sorry, but suggested word operators gave me shivers just seeing it :D I'm kind of feeling like it is better to not use a word operator at all if it as ambiguous as on, to or... I don't know what I don't like about calledAgainstBy, but I hate it really hard.

// I like this syntax
string::normalize()::toLowerCase()::slice(1);

// I'm kind of OK with this one...
string call normalize() call toLowerCase() call slice(1);

// but I'd rather write something like
string
 call normalize()
 call toLowerCase()
 call slice(1);

// or this
string
 ::normalize()
 ::toLowerCase()
 ::slice(1);

@bathos
Copy link

bathos commented Apr 1, 2022

@varna I don’t think we were meant to like calledAgainstBy: the list where it appeared was open speculation to explore another angle, I think, rather than a specific suggestion being advocated for. I used it as an example a few times only because it was the most over-the-top :)

@theScottyJam
Copy link
Contributor

I'm kind of feeling like it is better to not use a word operator at all if it as ambiguous as on, to

I do agree that we lose some value with generic words like to, because they really don't carry much meaning with them, and can be interpreted in a lot of ways. We still get the advantage that we're not using ascii symbols here, but we loose out on a chance to make the operator a little more explicit. Of course, anything else is going to be a bit more verbose, and may make some people shutter (e.g. with @bathos's use case of needing to use it all over the place). This is why I liked sendTo, as it still felt relatively concise, while conveying a little bit more meaning than a simple to, but I can certainly understand that people who are trying to protect against globalThis mutations won't particularly like how cumbersome sendTo can be. For everyone else, I feel it's appropriate in verbosity.

But, perhaps because of the use case that @bathos brought up, a word operator really wouldn't be appropriate.

@bathos
Copy link

bathos commented Apr 1, 2022

Forgot to mention important fine print earlier: due to extremely incorrect priorities, there is a keyword available for use here that I would embrace without hesitation.

@hax
Copy link
Member

hax commented May 27, 2022

One more thing with a text operator is that it might look strange when mixing them with optional chaining

I would prefer only use ? not ?., because ~> already play the role of ., so the syntax would be:
value?~>method(...args), and so on.

@hax
Copy link
Member

hax commented May 27, 2022

I don't like value to method(), because normally infix keywords with spaces (in, instanceof, as in TS, is in many languages) means low precedences, so people tend to parse
a.b to c().d() to (a.b) to (c().d()).

And such syntax actually make the call-this look like pipe.

@Mouvedia
Copy link

concerning :: tc39/proposal-bind-operator#27 and tc39/proposal-bind-operator#36 are relevant

@ljharb
Copy link
Member

ljharb commented Aug 18, 2022

Relevant, but E4X and JScript continue to have no impact on modern syntax choice.

@js-choi
Copy link
Collaborator Author

js-choi commented Aug 18, 2022

I would say that E4X and JScript have a slight impact on this proposal…in the same way that C, C++, Python, Ruby, Perl, and other languages that use :: also have a slight impact on this proposal. In other words, they are very slight precedents that might affect the intuition of new developers. We can keep them in mind as factors of education or intuition.

Having said that, from a web-compatibility perspective, E4X and JScript indeed have almost certainly no impact. After all, there is no engine in existence that supports hypothetical programs simultaneously using both ES4 or JScript and ES2015+ syntaxes.

@ljharb
Copy link
Member

ljharb commented Aug 18, 2022

Thanks, that’s much more precisely stated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bikeshedding Question about naming or something similar
Projects
None yet
Development

No branches or pull requests