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

Syntax #2

Open
chicoxyzzy opened this issue Oct 21, 2019 · 85 comments
Open

Syntax #2

chicoxyzzy opened this issue Oct 21, 2019 · 85 comments

Comments

@chicoxyzzy
Copy link
Member

chicoxyzzy commented Oct 21, 2019

Possible solutions

Arrow function syntax

// Irregular
() =*> ...

// not the same order as in regular generator functions
() =>* ...

// also wrong order
() *=> ...

// ASI hazard
*() => ...

Introduce new generator keyword for both function and arrow function

generator function() {}
const foo = async generator function() {};

class Foo {
  x = 1
  // No more ASI hazard!
  generator foo() {}
}

Previous discussions https://github.com/tc39/proposals/issues/216

@jridgewell
Copy link
Member

jridgewell commented Oct 21, 2019

@littledan and I had some hallway-track conversations about this. My thoughts were to add a gen keyword (much like the async keyword), and treat the * sigil as the ugly wart it is:

gen function foo() {} // same as function* foo() {}
foo = gen () => {};

class Foo {
  x = 1
  // No more ASI hazard!
  gen foo() {}
}

When creating an async generator, the order would be async gen.

@chicoxyzzy
Copy link
Member Author

chicoxyzzy commented Oct 21, 2019

There is an ASI hazard here

foo = gen ()
  => {};

@jridgewell
Copy link
Member

That's a syntax error currently, right? I don't understand how it would cause a new ASI hazard.

@MaxGraey
Copy link

MaxGraey commented Oct 21, 2019

I can imagine only two variants:
1.

foo = (*)(arg) => {};
foo = (*(arg)) => {};

second could be simplified for one / zero arguments as:

foo1 = (*arg) => {};
foo0 = (*) => {};

@ljharb
Copy link
Member

ljharb commented Oct 21, 2019

Adding a new gen is interesting, but there's no value imo in abbreviating - why not generator? (it's not func or async fn etc)

@MaxGraey
Copy link

More radical solution is using different brackets like [] instead ():

foo1 = [] => {}
foo2 = [[first, second]] => {}  // with array destruction
foo3 = [a, b] => {}

@chicoxyzzy
Copy link
Member Author

TBH, any of *=>, =>* or =*> look totally fine and clear to me.

@bathos
Copy link

bathos commented Oct 21, 2019

*=> seems least surprising to me, with =*> seeming least obvious (it helps to still have ‘the arrow’ for recognition).

@jridgewell
Copy link
Member

Adding a new gen is interesting, but there's no value imo in abbreviating - why not generator?

Either is fine with me.

*=> seems least surprising to me, with =*> seeming least obvious (it helps to still have ‘the arrow’ for recognition).

Least surprising, but still super ugly. I (as a relatively experienced JS dev) still struggle with the order for function * foo() {} ("is it before function, in between, or after the identifier?"). I imagine new devs are just as confused ("what does the star mean?").

A keyword would be less esoteric, and much easier to Google for. So extending generator support to arrow functions gives us a chance to tackle both with one proposal. 😃

@rumkin
Copy link

rumkin commented Oct 21, 2019

I vote for keyword gen and *=>.

@rumkin
Copy link

rumkin commented Oct 21, 2019

@ljharb

Adding a new gen is interesting, but there's no value imo in abbreviating - why not generator? (it's not func or async fn etc)

There is the answer in your question: as async is a shortening of asynchronous, thus gen could stand for generator.

@janwirth
Copy link

janwirth commented Oct 21, 2019

I vote for

const myGen = async gen () => yield await sth()
// or
async gen function myGen () { yield await sth() }

It I think it is more writeable readable than *:

  • readable because you will not need to keep arbitrary special characters in his mental memory.
  • writeable becauses async is the adjective and gen is the noun in our expression if read as a sentence. Like this we are closer to english grammar.

Thoughts on oother programming languages

  • Haskell - it is very hard to read for beginners as sigils are used to create a DSL for working algebraic data types. It is hard to google for the meaning of sigils. Elm has very limited use of sigils and is much more readable documentation is more retrievable IMO
  • PHP makes extensive use of $. I find it just makes variable less legible as you need too visually segment the sigil and the variable name.

@ljharb
Copy link
Member

ljharb commented Oct 21, 2019

@rumkin async is a well-known abbreviation for asynchronous, and is effectively a word on its own; gen is not.

@janwirth
Copy link

janwirth commented Oct 21, 2019

@ljharb - I agree. I also think it is easier to type and thus less useful to abbreviate.

Researches found that "Shorter identifier names take longer to comprehend"
https://link.springer.com/article/10.1007%2Fs10664-018-9621-x

@bathos
Copy link

bathos commented Oct 21, 2019

If a keyword is introduced for the arrow case, does it necessarily mean also introducing it for the ‘longhand’ case?

@ljharb
Copy link
Member

ljharb commented Oct 21, 2019

For consistency, I would hope so.

@janwirth
Copy link

If we look for inspiration in python, we notice that they have no keyword to declare a generator - it is implied by yield

@rumkin
Copy link

rumkin commented Oct 21, 2019

https://link.springer.com/article/10.1007%2Fs10664-018-9621-x

@FranzSkuffka, I think this work isn't relevant for well-known language syntax. It's about new identifiers. Such identifiers are always located in brain's short memory and meaningful names help our brain to build an abstract model faster.

@chicoxyzzy
Copy link
Member Author

chicoxyzzy commented Oct 22, 2019

Added generator keyword as a possible solution to the README.md and to the first message of this issue

@inoyakaigor
Copy link

inoyakaigor commented Oct 23, 2019

How about const *asd = () => {}?

@bathos
Copy link

bathos commented Oct 23, 2019

That’s between a const keyword and a const binding identifier. The arrow function is the part in the initializer, after the equals sign. These expressions can appear in many places, not just in const declarations.

@Jamesernator
Copy link

Is there any ASI hazard to *() => {} other than a lonely *() => {} expression statement? If not I don't really think it's compelling to discount that.

@constb
Copy link

constb commented Oct 24, 2019

hi! why not *>? like (x, y, z) *> { … } and async (x, y, z) *> { … }

@jashsayani
Copy link

Functions have gone from function foo(bar) {} to const foo = (bar) => {}.

Hence it feels like function* generator(i) {} would be const* generator = (i) => {} but this clearly opens up a can of worms (like people typing const* a = 1 and would annoy people from other languages familiar with pointers. So ignoring that, *() => {} makes the most sense intuitively.

@bathos
Copy link

bathos commented Dec 10, 2019

@Jamesernator

IIUC that’s the only case, yeah. The following would parse as a MultiplicativeExpression with an invalid right hand side:

foo
* () => {};

The asterisk would match MultiplicativeOperator and the parentheses would match CoverParenthesizedExpressionAndArrowParameterList. If the covered part refined successfully to ParenthesizedExpression (e.g. * (a)), then failure would occur at =>; otherwise it would occur at the parens themselves. Either way, no arrow function.

That said, I wouldn’t have called this a hazard: it would throw a SyntaxError up front. It’s not a ‘trap’ like

foo
[bar]

The extent of the ‘hazard’ is just that a semicolon is necessary. The absence of one poses no risk of producing code that evaluates at all, much less with a different meaning from what was intended. (AFAICT)

@jamiebuilds
Copy link
Member

jamiebuilds commented Jan 2, 2020

Does the keyword option have to be a variant on the word generator?

When teaching them, the word "Generators" has caused a lot of confusion in how they relate to "Iterators". So I started referring to them as "Iterator Functions" and that seemed to help people understand their relationship.

So I think iterator could be a good alternative choice for a keyword.

But even better than that: "Iter" is already a well-established abbreviation for "Iterator" so the keyword iter could work:

iter function fn() {...}
async iter function fn() {...}

@bathos
Copy link

bathos commented Jan 3, 2020

I would think that making the keyword for defining generator functions iter would increase, not decrease, confusion regarding the subset-superset relationship between generators and iterators, no?

@jamiebuilds
Copy link
Member

Maybe, but as it stands right now the relationship isn't seen by many. Anecdotally, I've talked to developers who describe generators as "pausable functions" (not sure where this comes from) or "how async functions are implemented under the hood" (which I think comes from transpilers), and I've seen them use the iterator protocol directly with while loops and calling .next() instead of using for..of. I suspect generators are underutilized (not that they are something every developer would use daily) for this reason.

@ljharb
Copy link
Member

ljharb commented Jan 3, 2020

A generator is a pauseable (synchronous) function. It also produces an iterator (but isn’t one). They are (sadly) the dominant implementation detail for transpiler output of async/await; i think that’s indeed where that one comes from.

given that the syntax is async function but it returns a Promise, the keyword seems to suggest what it is and not what it produces. Similarly, I’d expect a generator keyword not to be about the iterator it produces, but about what it is (a value generator).

@bathos
Copy link

bathos commented Jan 3, 2020

I think I agree with the gist of the previous comment, but would point out that a ‘generator function’ produces a generator and that a generator is a specific kind of iterator. The function itself isn’t an iterator or a generator, at least not as the spec defines those terms.

@ljharb
Copy link
Member

ljharb commented Aug 25, 2021

It's not personal preference - the word you're looking for is "anecdotal". The way I know is because I've seen people use the abbreviation "async" for "asynchronous" quite frequently, predating async/await - node.js is full of APIs suffixed with "Sync", and the others are always referred to as "async", for example - and i have never once, not ever, seen someone use the abbreviation "gen" for "generator". It's quite possible you, or someone else, have seen this usage, and I'd love to hear about it - but it's not an unreasonable assertion. It's just not an objective one.

I completely agree with you that gen is way more descriptive than an asterisk, as is generator, and that either would have been a better choice than the asterisk in ES6.

@alfnielsen
Copy link

First: Why is this threat discussion about star or not? (replacement of star)

This should not be a discussion about generator functions in javascript in general,
(I do agree that the *is not the best choice )
This is about getting generator arrow functions into the language, which I guess we all want (as fast as possible!)

Changing the star to something else, would be something that the "normal" generator function also should apply to,
aka that discussion is another language threat proposal!

There are already a syntax in the language for generator method (whether people like that syntax or not!)

Syntax for a function is: (? are optional)

  • function *? name? ( (arguments)? ) { (body)? }
  • *(optional - turns the function into a generator function)

Syntax for an arrow function are: (with some special case like single return etc..)

  • ( (arguments)? ) => { (body)? }

To follow the same syntax as a normal function and not introduce multiple language syntax for the same functional logic,
the star should be place as in a normal function aka:

  • *? ( (arguments)? ) => { (body)? }

I have a hard time undestanding why this is even up for discussion ?

(again changing the star is something I like to see, but it has nothing to do with getting generator arrow function into the language )

@mvduin
Copy link

mvduin commented Sep 9, 2021

Agreed, *( args ) => ... is the only syntax consistent with existing generators. The only argument against it (that applies specifically to this syntax and not to function*) is the ASI hazard.

I previously posted a comment about that in #8 but this is a better place for it:


ASI is the awful plague of Automatic Semicolon Insertion. The problem here is that ASI is only done if not inserting a semicolon before the next token would result in a syntax error, while in this situation

x = y
*(z) => {}

the parser will happily accept x = y * (z) before it hits the => at which point it would have to go back and insert the semicolon before the *. I don't know if this would be genuinely problematic, but it would be unlike existing ASI rules.

However, I think this ASI hazard can also just be ignored since there's no reason to ever put an arrow-function/generator expression at the start of an ExpressionStatement, since this is grammatically only permitted if the arrow body is followed by a comma or a semicolon and in either case the function is discarded.

Note for comparison that it's not even possible for an ExpressionStatement to start with a non-arrow function/generator expression, since attempting to do so result in it being parsed as a function/generator declaration instead:

function() {};
^^^^^^^^

Uncaught SyntaxError: Function statements require a function name

This despite the fact that putting a non-arrow function expression at the start of an ExpressionStatement would (unlike the arrow case) not be useless, since function(){}() is (horrifyingly) valid syntax for a function call (as long as you don't put it at the start of a statement).

 

BTW there's no grammatical ambiguity in 1 * () => {} since the literal 1 cannot be followed by an expression (other than a TemplateLiteral), nor can the * operator be followed by an arrow function expression for that matter.

Currently the only context where the grammar allows an ArrowFunction after a * token is as argument to yield*, which means yield *() => {} would be ambiguous with yield* () => {}. However, while the latter is currently syntactically valid, it is semantically nonsense and will always cause a runtime error (() => {} is not iterable), so it is safe to eliminate this interpretation and thus the ambiguity. I'd also be fine with disallowing both interpretations and require writing yield (*() => {}).


In conclusion, the ASI hazard seems like a complete non-issue to me.

@pepkin88
Copy link

However, while the latter is currently syntactically valid, it is semantically nonsense and will always cause a runtime error (() => {} is not iterable)

But you could make it iterable with Function.prototype[Symbol.iterator] = function* () {}, so yield* () => {} will not always cause an error.

@mvduin
Copy link

mvduin commented Sep 11, 2021

But you could make it iterable with Function.prototype[Symbol.iterator] = function* () {}

Ew, that's so cursed. I'd sincerely hope there's noone out there who has actually done that, but I guess it would also be an option to just keep parsing yield *() => {} greedily as yield* () => {}. Making it a syntax error and requiring disambiguation by writing yield (*() => {}) or yield* (() => {}) would allow for a more helpful parse-time error rather than risk unexpected runtime behaviour, at the cost of a minor (and easily fixable) backwards incompatibility for code that makes Function.prototype iterable to use yield* on arrows, if there's any out there.

@Luxcium
Copy link

Luxcium commented Sep 14, 2021

TBH, any of *=>, =>* or =*> look totally fine and clear to me.

if I « think outside the box » why can't we use =>>

@mikabytes
Copy link

if I « think outside the box » why can't we use =>>

"the psychedelic syntax" :)

@dead-claudia
Copy link

dead-claudia commented Aug 14, 2022

Regarding *() => {} having a potential ASI hazard, I do want to point out a couple things:

  1. I personally anticipate approximately nobody is going to try to make an expression statement out of a generator function literal. Forcing it to be wrapped in parentheses is okay IMHO, and encoding this in the grammar this is as easy as just adding a negative lookahead for * in the definition of an expression statement. Edit: A similar situation exists with yield * ( ) => { } (a case that could reasonably see actual use in the wild), but one could similarly require parentheses here to disambiguate (and also preferably require them for yield* (*() => {}) as well to avoid unintentionally preventing a potential future yield** expr construct).
  2. Class generator methods already have an ASI hazard that's nearly identical, and I find myself running into that a lot more as I generally code without semicolons. (This is part of what's led me to centralize all my property declarations above the constructor as a general practice, including bound methods.)

Personally, I feel the ASI hazard concern is a bit overblown for this.

Edit 2: I notice after I send this that it's restating much of #2 (comment). This does add a few more things to round out the idea, though.

@conartist6
Copy link

One problem with all the *=> and *> etc is that they look really nice in code fonts where * is middle aligned but pretty weird in normal fonts. If you discount the idea that the syntax looks pretty because all the symbols line up perfectly, then *() => {} seems like the more natural syntax to me.

@conartist6
Copy link

BTW there's no grammatical ambiguity in 1 * () => {} since the literal 1 cannot be followed by an expression (other than a TemplateLiteral), nor can the * operator be followed by an arrow function expression for that matter.

It is ambiguous. 4 * function(){} === NaN, and must continue to.

@mvduin
Copy link

mvduin commented Nov 15, 2022

It is ambiguous. 4 * function(){} === NaN, and must continue to.

Ehm, my statement was about arrows, not about non-arrow function expressions. The current grammar does not syntactically allow an arrow function expression as operand to the * operator:

4 * () => {}
  ^^^

Uncaught SyntaxError: Malformed arrow function parameter list

@dead-claudia
Copy link

image

Just for quick reference. I reproduced it likewise in Chrome's dev tools.

My personal preference for this is still to require such yields to be parenthesized to disambiguate, which ultimately wouldn't result in changing the meaning of any currently syntactically legal productions.

@sukima
Copy link

sukima commented Apr 13, 2023

This is already an old and long discussion but the goal stated in the beginning is still worth while. I'd like to refocus and offer my perspective. For me a generator arrow function is not about one-liners or easier to read semantic (though these are an added benefit for me) but the auto-binding of this. As it stands though regardless of syntax this really sucks:

let that = this;
function * foo() {
  that.nowWeHaveBinding
}

And in most cases where I wish I had an arrow-function generator is in that situation when I want to make a generator in my closure but have the this bound properly.

@cowboyd
Copy link

cowboyd commented May 2, 2023

I'd like to add my $.02 and concur with @mvduin and @dead-claudia. In addition to the reasons they put forward, ubiquitous integration of linters, formatters, language servers, and particularly TypeScript makes the ASI hazard feel like it's receding from 2023 close to, if not past, the vanishing point.

The fact is that *() => is the only option that is perfectly aligned with intuition. As for gen, generator, etc... They are great in a green-fields, context free situation, but * is what we have, and * is what we will always have, so combining it with the existing arrow syntax in a manner consistent with all the other ways to declare a generator function is the only option that does not introduce a cognitive dissonance.

@SpadeAceman
Copy link

The fact is that *() => is the only option that is perfectly aligned with [my personal] intuition.

Fixed. 🙂

The fact is that my personal intuition regards function and => as the function signifiers, and expects the generator asterisk to be closely associated with them in some way (as is currently the case with function*):

  • function --> =>
    • function(){} --> () => {}
  • function* --> =>*, or *=>, or =*> (my preference)
    • function*(){} --> () =>* {}, or () *=> {}, or () =*> {}

In contrast, placing the generator asterisk next to the arguments list of an arrow function ((*() => {})) isn't intuitive to me, because it disassociates the generator asterisk from the function arrow.

Ultimately (assuming this proposal eventually moves forward), syntax will be selected that will require one of us to adjust our intuitions. I'm simply pointing out that intuition can be subjective, and explaining how I (and others here) have reasonably arrived at different syntax suggestions.

As for gen, generator, etc... They are great in a green-fields, context free situation, but * is what we have […]

On this point, we agree. 🙂

@mvduin
Copy link

mvduin commented May 4, 2023

The fact is that my personal intuition regards function and => as the function signifiers, and expects the generator asterisk to be closely associated with them in some way (as is currently the case with function*)

Except we also already have the *name(){} syntax for generator methods in classes and object literals, which lacks any explicit function-signifier.

@conartist6
Copy link

@mvduin yes but that construct is understood to be purely a syntactic sugar for name: function*(){}.

There's no need to sugar the arrow version like that because you'd just write name: () *=> {} (or whatever syntax is adopted). That works basically as well as the sugared shorthand because in the modern version of the language the anonymous-looking function is actually named name.

@cowboyd
Copy link

cowboyd commented May 4, 2023

Ultimately (assuming this proposal eventually moves forward), syntax will be selected that will require one of us to adjust our intuitions.

Agree. All the strange gnobbles of the original => syntax like () in relation to argument list and {} around body were things that everybody eventually seemed to move past, and this seems minor in comparison, so I would much rather see anything than nothing.

That said, I do think that given the full breadth of how functions are expressed in JavaScript, the bit that marks the function variant appears very much in the head position. I think this is where a lot of the appeal of the gen/generator proposals comes from. Viewed from this perspective, it's very much the original function* syntax that is the only real outlier when viewed along side a *() => and gen () => {}

Type Normal Async Generator (gen) Generator (*)
Short () => {} async () => {} gen () => {} *() => {}
Method method() {} async method() {} gen method() {} *method() {}
Dynamic [property]() {} async [property]() {} gen [property]() {} *[property]() {}
Long function () {} async function() {} gen function() {} function*() {} 🤔

Which is not to say that it isn't more intuitive in the individual context of a expressing a generator lambda , but it does seem to me that by not following this convention, you are introducing not just another sequence to parse and assign meaning to (=*>), but you're also going against the prevailing current of not placing function type at the head of the expression.

Type Normal Async Generator (gen) Generator (*)
Short () => {} async () => {} gen () => {} () =*> {} 🤔
Method method() {} async method() {} gen method() {} *method() {}
Dynamic [property]() {} async [property]() {} gen [property]() {} *[property]() {}
Long function () {} async function() {} gen function() {} function*() {} 🤔

Rather than "aligned with intuition", I think it is safer to say more narrowly that *() => {} is aligned with the existing forms of expressing functions.

@dead-claudia
Copy link

I still stand by my assessment that *() => ... isn't an issue.

It also neatly follows the existing patterns with generators, so people would know it right away.

@ljharb
Copy link
Member

ljharb commented May 4, 2023

@conartist6 it is not purely sugar; concise methods can't be constructed (they have no .prototype) and they can use super.method syntax.

@bathos
Copy link

bathos commented May 4, 2023

(minor nit: arrows can also use that syntax, but only lexically & depending on the outer env they close over. point remains of course!)

@pepkin88
Copy link

pepkin88 commented May 4, 2023

you are introducing not just another sequence to parse and assign meaning to (=*>)

I personally wouldn't say it is a bad thing here. The symbol is derived from an existing one, it looks like a variant of =>. And with =>* or *=>, the arrow part is left intact.

With *() => {} you have an asterisk * first, then a parameter list, then the arrow. With =>* the whole indicator of a generator function is in one place.

Someone may say, that with async functions async and => are also separated. True, but the word async doesn't show up in other contexts with a different meaning. The * symbol is quite overloaded, primarily used for multiplication, and I feel that might be the reason, why I don't quite like the *() => {} syntax.

Another reason is the variant with one parameter:
*param => { body }
vs
param =>* { body }
The first one looks to me like it is the param that is modified by *, not the function.


but you're also going against the prevailing current of not placing function type at the head of the expression.

In my perception, this kind of already happened with all arrow functions, where instead of the word function, the indicator of "functionality" is an arrow => placed after parameters. So in my view, placing an asterisk on the right of the function indicator (=>) is a consistent choice for a generator function syntax: function*=>*.


In the end though, whichever syntax will be chosen, people will get used to it.

@cowboyd
Copy link

cowboyd commented Jul 31, 2023

It would be amazing to see some movement on this in one direction or the other. What would it take?

@cowboyd
Copy link

cowboyd commented Nov 3, 2023

This feels part and parcel of a category of asymmetries that leave generators in the lurch. I find myself wanting higher order iterators more and more.

for yield* (let item of iterable) {}

or the like

@dead-claudia
Copy link

This feels part and parcel of a category of asymmetries that leave generators in the lurch. I find myself wanting higher order iterators more and more.

for yield* (let item of iterable) {}

or the like

@cowboyd You could just do nested for loops for that. Also, the consumption side of that has little to do with this bug.

@cowboyd
Copy link

cowboyd commented Nov 6, 2023

This feels part and parcel of a category of asymmetries that leave generators in the lurch. I find myself wanting higher order iterators more and more.

for yield* (let item of iterable) {}

or the like

@cowboyd You could just do nested for loops for that. Also, the consumption side of that has little to do with this bug.

I agree, it has little to do with this bug. I'm more pointing out that there are a lot of asymmetries where generators are like second class citizens compared to async/await

@gurgunday
Copy link

Instead of discussing introducing new syntax like generator that would also need to work with the function keyword, let's focus on moving this proposal forward without affecting other things

What matters is, today, any of the following is understandable by someone who already knows that function* creates a GeneratorFunction object:

// Irregular
() =*> ...

// not the same order as in regular generator functions
() =>* ...

// also wrong order
() *=> ...

// ASI hazard
*() => ...

It's as simple as combining the token that creates a function with *

Let's confirm that the final syntax will be one of these to limit the scale of the discussion

Brendan Eich just reaffirmed his support for this proposal too, so there is no better time to get actual momentum 🤠🚀

Just to make sure, @hemanth, are you still championing it?

@dead-claudia
Copy link

I'm still not convinced the ASI hazard is a real issue with the *() => ... syntax. To avoid ASI ambiguity, just do a negative lookahead for * when parsing for expression statements (or in other words, don't allow * to start such a statement). This is the same mechanism used to distinguish object patterns from blocks.

If you do that, the ASI hazard goes away, no cover grammar needed.

The only other hazard is yield * () => x. This will need a similar negative lookahead for yield (but not yield*) to avoid ambiguity, letting that sequence continue to evaluate as yield* (() => x) for compatibility as it does today.

Other than that, I don't see anything else that could be in the way of the syntax *() => ....

@cowboyd
Copy link

cowboyd commented Feb 13, 2024

@gurgunday I agree whole heartedly that it is important to focus, and my vote would be the same as @dead-claudia:

*() => ... FTW

I don't actually have a vote, but as someone who spends most days using JavaScript generators I do have an outsized interest.

So yes, winnowing the discussion to one of the options you've outlined would be a great first step. So how does this decision to winnow get made? Assume it would take some action from @chicoxyzzy?

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