特征可以在 Scala 3 中有辅助构造函数吗?

Can traits have secondary constructors in Scala 3?

我从 Auxiliary Class Constructors 文章中复制了以下代码,粘贴到 Scastie 中,将 class 更改为 trait 并将 Scala 版本设置为 3.1.0:

val DefaultCrustSize = 12
val DefaultCrustType = "THIN"

// the primary constructor
trait Pizza (var crustSize: Int, var crustType: String) {

    // one-arg auxiliary constructor
    def this(crustSize: Int) = {
        this(crustSize, DefaultCrustType)
    }

    // one-arg auxiliary constructor
    def this(crustType: String) = {
        this(DefaultCrustSize, crustType)
    }

    // zero-arg auxiliary constructor
    def this() = {
        this(DefaultCrustSize, DefaultCrustType)
    }

    override def toString = s"A $crustSize inch pizza with a $crustType crust"

}

Here's the result. 它给出了以下错误:

org.scalameta.invariants.InvariantFailedException: invariant failed:
when verifying scala.meta.classifiers.`package`.XtensionClassifiable[scala.meta.Template](templ)(scala.meta.Tree.classifiable[scala.meta.Template]).is[Template.Quasi](Template.this.Quasi.ClassifierClass[scala.meta.Template]).||(templ.stats.forall(((x: scala.meta.Stat) => scala.meta.classifiers.`package`.XtensionClassifiable[scala.meta.Stat](x)(scala.meta.Tree.classifiable[scala.meta.Stat]).is[Ctor](scala.meta.Ctor.ClassifierClass[scala.meta.Stat]).`unary_!`)))
found that scala.meta.classifiers.`package`.XtensionClassifiable[scala.meta.Template](templ)(scala.meta.Tree.classifiable[scala.meta.Template]).is[Template.Quasi](Template.this.Quasi.ClassifierClass[scala.meta.Template]) is false
and also templ.stats.forall(((x: scala.meta.Stat) => scala.meta.classifiers.`package`.XtensionClassifiable[scala.meta.Stat](x)(scala.meta.Tree.classifiable[scala.meta.Stat]).is[Ctor](scala.meta.Ctor.ClassifierClass[scala.meta.Stat]).`unary_!`)) is false
where Template = scala.meta.Template$@7759c8f1
where templ = {

    // one-arg auxiliary constructor
    def this(crustSize: Int) = {
        this(crustSize, DefaultCrustType)
    }

    // one-arg auxiliary constructor
    def this(crustType: String) = {
        this(DefaultCrustSize, crustType)
    }

    // zero-arg auxiliary constructor
    def this() = {
        this(DefaultCrustSize, DefaultCrustType)
    }

    override def toString = s"A $crustSize inch pizza with a $crustType crust"

}
    at org.scalameta.invariants.InvariantFailedException$.raise(Exceptions.scala:19)
    at scala.meta.Defn$Trait$.internal7(Trees.scala:432)
    at scala.meta.Defn$Trait$.apply(Trees.scala:425)
    at scala.meta.internal.parsers.ScalametaParser.$anonfun$traitDef(ScalametaParser.scala:4592)
    at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
    at scala.meta.internal.parsers.ScalametaParser.traitDef(ScalametaParser.scala:4570)
    at scala.meta.internal.parsers.ScalametaParser.tmplDef(ScalametaParser.scala:4548)
    at scala.meta.internal.parsers.ScalametaParser.defOrDclOrSecondaryCtor(ScalametaParser.scala:4201)
    at scala.meta.internal.parsers.ScalametaParser.nonLocalDefOrDcl(ScalametaParser.scala:4172)
    at scala.meta.internal.parsers.ScalametaParser$$anonfun$templateStat.applyOrElse(ScalametaParser.scala:5120)
    at scala.meta.internal.parsers.ScalametaParser$$anonfun$templateStat.applyOrElse(ScalametaParser.scala:5114)
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
    at scala.meta.internal.parsers.ScalametaParser.statSeq(ScalametaParser.scala:5057)
    at scala.meta.internal.parsers.ScalametaParser.templateStats(ScalametaParser.scala:5130)
    at scala.meta.internal.parsers.ScalametaParser.templateStatSeq(ScalametaParser.scala:5110)
    at scala.meta.internal.parsers.ScalametaParser.$anonfun$templateBody(ScalametaParser.scala:4945)
    at scala.meta.internal.parsers.ScalametaParser.inBraces(ScalametaParser.scala:800)
    at scala.meta.internal.parsers.ScalametaParser.templateBody(ScalametaParser.scala:4945)
    at scala.meta.internal.parsers.ScalametaParser.templateBodyOpt(ScalametaParser.scala:4953)
    at scala.meta.internal.parsers.ScalametaParser.template(ScalametaParser.scala:4887)
    at scala.meta.internal.parsers.ScalametaParser.$anonfun$template(ScalametaParser.scala:4906)
    at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
    at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
    at scala.meta.internal.parsers.ScalametaParser.template(ScalametaParser.scala:4891)
    at scala.meta.internal.parsers.ScalametaParser.$anonfun$templateOpt(ScalametaParser.scala:4931)
    at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
    at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
    at scala.meta.internal.parsers.ScalametaParser.templateOpt(ScalametaParser.scala:4923)
    at scala.meta.internal.parsers.ScalametaParser.$anonfun$objectDef(ScalametaParser.scala:4682)
    at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
    at scala.meta.internal.parsers.ScalametaParser.objectDef(ScalametaParser.scala:4674)
    at scala.meta.internal.parsers.ScalametaParser.tmplDef(ScalametaParser.scala:4558)
    at scala.meta.internal.parsers.ScalametaParser.topLevelTmplDef(ScalametaParser.scala:4540)
    at scala.meta.internal.parsers.ScalametaParser$$anonfun$topStat.applyOrElse(ScalametaParser.scala:5079)
    at scala.meta.internal.parsers.ScalametaParser$$anonfun$topStat.applyOrElse(ScalametaParser.scala:5067)
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
    at scala.meta.internal.parsers.ScalametaParser.statSeq(ScalametaParser.scala:5057)
    at scala.meta.internal.parsers.ScalametaParser.topStatSeq(ScalametaParser.scala:5066)
    at scala.meta.internal.parsers.ScalametaParser.$anonfun$batchSource(ScalametaParser.scala:5297)
    at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
    at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
    at scala.meta.internal.parsers.ScalametaParser.batchSource(ScalametaParser.scala:5256)
    at scala.meta.internal.parsers.ScalametaParser.$anonfun$source(ScalametaParser.scala:5239)
    at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:888)
    at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:922)
    at scala.meta.internal.parsers.ScalametaParser.source(ScalametaParser.scala:5238)
    at scala.meta.internal.parsers.ScalametaParser.entrypointSource(ScalametaParser.scala:5244)
    at scala.meta.internal.parsers.ScalametaParser.$anonfun$parseSource(ScalametaParser.scala:143)
    at scala.meta.internal.parsers.ScalametaParser.parseRule(ScalametaParser.scala:53)
    at scala.meta.internal.parsers.ScalametaParser.parseSource(ScalametaParser.scala:143)
    at scala.meta.parsers.Parse$.$anonfun$parseSource(Parse.scala:29)
    at scala.meta.internal.parsers.ScalametaParser$$anon4.apply(ScalametaParser.scala:5308)
    at scala.meta.parsers.Api$XtensionParseDialectInput.parse(Api.scala:25)
    at scala.meta.parsers.Api$XtensionParseInputLike.parse(Api.scala:14)
    at com.olegych.scastie.instrumentation.Instrument$.apply(Instrument.scala:199)
    at com.olegych.scastie.instrumentation.InstrumentedInputs$.apply(InstrumentedInputs.scala:23)
    at com.olegych.scastie.sbt.SbtProcess$$anonfun.applyOrElse(SbtProcess.scala:191)
    at com.olegych.scastie.sbt.SbtProcess$$anonfun.applyOrElse(SbtProcess.scala:177)
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
    at akka.actor.FSM.processEvent(FSM.scala:707)
    at akka.actor.FSM.processEvent$(FSM.scala:704)
    at com.olegych.scastie.sbt.SbtProcess.processEvent(SbtProcess.scala:64)
    at akka.actor.FSM.akka$actor$FSM$$processMsg(FSM.scala:701)
    at akka.actor.FSM$$anonfun$receive.applyOrElse(FSM.scala:695)
    at akka.actor.Actor.aroundReceive(Actor.scala:539)
    at akka.actor.Actor.aroundReceive$(Actor.scala:537)
    at com.olegych.scastie.sbt.SbtProcess.aroundReceive(SbtProcess.scala:64)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:614)
    at akka.actor.ActorCell.invoke(ActorCell.scala:583)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:268)
    at akka.dispatch.Mailbox.run(Mailbox.scala:229)
    at akka.dispatch.Mailbox.exec(Mailbox.scala:241)
    at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

为什么?为什么它在第 1 行之后,看起来非常好并且在我删除其余部分时被接受? “不变失败”是否意味着它是编译器中的错误? (在这种情况下,许多其他编译器会在消息中添加鼓励报告的内容。)

主要问题:

The documentation on trait parameters只提到trait参数,没有提到trait构造函数:

Scala 3 allows traits to have parameters, just like classes have parameters.

它还链接到原始 SIP 文档以供参考:

For more information, see Scala SIP 25.

SIP-25 – trait 参数 中,它说 [粗体 强调我的]:

In the ClassDef of traits, we still do not allow secondary constructors.

然而,这种限制并没有体现在 Syntax Summary 中,它实际上并没有在句法上区分 classes 和 traits。因此,限制纯粹是文档之一,而不是语法规范。

所以,回答你的问题:

Why?

因为您的代码在语法上有效但在语义上无效,并且 ScalaMeta 似乎不期望这种特定类型的语义无效。

And why is it after line 1, which seems perfectly fine and gets accepted when I remove the rest? Does "invariant failed" mean that it's a bug in the compiler? (Many other compilers in such cases add to the message an encouragement to report it.)

这显然不是编译器中的错误,因为异常不是在编译器中抛出,而是在 ScalaMeta 中抛出。

Main questions:

  • Are secondary (or auxiliary – IIUC the two terms I've seen used apparently interchangeably mean the same) constructors allowed in traits?

没有。 SIP-25 显然不允许它们。