E07835: 'collect as T' needs T to have a no-argument constructor that can be called where the pipeline is written
What went wrong
'| collect as T' constructs a NEW T with its no-argument constructor - exactly as if 'T()' were written where the pipeline is - and then pipes each item into it through its '|' operator. So T must have a no-argument constructor, and that constructor must be one that can be called from where the pipeline is written. One of the two is not true here: the message names T and says which.
- "T has no no-argument constructor": a type that declares only constructors WITH parameters still gets a generated no-argument one, so the usual cause is a sub-type of a class whose no-argument constructor is private - no no-argument constructor is generated for the sub-type, because its implicit 'super()' could not reach the private one.
- "T's no-argument constructor is private (or protected)": it exists, but 'T()' could not be written here either. A 'default private' no-argument constructor is how a type whose own constructors set a property is allowed to leave it unset (E07175), so making a T with it from outside the type would observe that unset property.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 954 (How do I use collect as to accumulate stream items into a...)
Pattern
The 'collect as' pattern works in three steps: create an instance with no arguments ('T()'), pipe each item of the pipeline into it ('collector | item', which calls 'operator |'), and return the accumulated instance as the result. The type needs BOTH a no-argument constructor callable where the pipeline is and an 'operator |' that accepts the pipeline's element type.
How to fix it
- Give T a public no-argument constructor that sets every property (or initialise every property where it is declared), or collect into a type made to accumulate and build the T from its result
- Inside T itself a private no-argument constructor IS callable, but one that leaves a property unset is still rejected (E08264)
- Check that 'operator |' is also defined (see E07830)
The [EXAMPLE] section below shows a Counter whose super's no-argument constructor is private (so Counter has none), and a Counter record that 'collect as' can make.
Common causes
- A sub-type of a class whose no-argument constructor is 'default private'
- A type whose own no-argument constructor is 'default private'
- Collecting into a type that was designed to be built only through a factory
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E07835: 'collect as T' needs T to have a no-argument constructor that can be called where the pipeline is written
#!ek9 defines module error.e07835.bad defines class Tally as open count as Integer? default private Tally() Tally() -> start as Integer this.count: start protected increment() count++ default operator ? //No no-argument constructor: Tally's is private, so none is generated here Counter extends Tally Counter() -> start as Integer super(start) operator | -> item as Integer if item? increment() defines function countAll() <- rtn as Counter? //'collect as' makes the Counter with no arguments rtn: for i in 1 ... 3 | collect as Counter
Fixed: Give the target type a public no-argument constructor
#!ek9 defines module error.e07835.good defines record Counter count as Integer: Integer() default operator ? operator $ as pure <- rtn as String: String() if count? rtn := $count operator | -> item as Integer if item? if not count? count: 1 else count++ defines program CountAll() stdout <- Stdout() result <- for i in 1 ... 3 | collect as Counter stdout.println($result)
Questions and answers
These Q&A entries show E07835 and its fix: