-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Background and motivation
It has been asked on Discord to support "upcast[ing] productions in a nonterminal", avoiding on fusers boilerplate upcasting to a common ancestor that is shared by a nonterminal containing the production.
Here is a real-world example of this boilerplate. Each production builder is finished with essentially dummy fusers1:
This has been recognized as a real usability issue of Farkle, and can be solved pretty easily.
API Proposal
namespace Farkle.Builder;
- public sealed class Production<T> {}
+ public interface Production<out T> {}Production<T> will continue having no members, reflecting the builder's philosophy that its objects are black boxes. The I prefix will not be added to signify that implementing this interface from user code will not make sense; just like with designtime Farkles. 2
API Usage
With the proposed change the code snippet above could be simplified to this (methodDeclaration and fieldDeclaration return different descendants of Statement.Definition):
var definition = Nonterminal.Create<Statement.Definition>("Method or Field Declaration List",
methodDeclaration.AsIs(),
fieldDeclaration.AsIs()
);Much terser and more intuitive (and a tiny bit more performant because AsIs is used). A dummy prototype of this API shape compiles as expected.
Alternative Designs
We could go further and implement Production<T> on DesigntimeFarkle<T>, making the example above to:
var definition = Nonterminal.Create<Statement.Definition>("Method or Field Declaration List",
methodDeclaration,
fieldDeclaration
);It would save even more characters, but it's not orthogonal to the distinction between designtime Farkles and productions; it's just a syntax convenience that needs a strong justification to puncture the architecture. And anyone interested can create a method that emulates the call above.
Risks
- It's a breaking change.
- We don't care, a new major version is underway, everything is subject to change, and this is not source-breaking.
- Users might implement it by mistake.
- It will be documented that implementing it from user code is not allowed, just like designtime Farkles.
Footnotes
-
The fuser was subsequently eliminated by calling
AsIswith an explicit type parameter (that was smart!) but only works on the AsIs extension method of designtime Farkles; not theAsIsinstance method of production builders. Even then, some boilerplate on each production remains. ↩ -
The
Farkle.PostProcessor<T>interface is an exception to this rule and makes sense to implement it from user code. It has been renamed toIPostProcessor<T>on Farkle 7. ↩