在此处scalafiddle
- 为什么 run 函数不能在 DSL [A] 特性中编译,但可以在外部工作?
- 在这种情况下,类型推断如何工作?
- why does the run function not compile in the DSL[A] trait, but worked outside?
- how does type inference work in this case?
这是广义抽象数据类型的情况.
This is a case of generalized abstract data type.
当您有 DSL [A] 且函数返回 A 时,编译器可以证明:
When you have a DSL[A] and function returning A, compiler can prove that:
- 用于 case GetLength A = Int ,因此您可以在那里返回 Int
- 用于 case ShowResult A = String ,因此您可以返回 String
- for case GetLength A=Int so you can return Int there
- for case ShowResult A=String so you can return String
但是,众所周知,Scala 2没有对GADT的完美支持,因此有时编译器即使可以工作也会失败.我猜有些编译器开发人员可以弄清楚确切的情况,但是有趣的是,它可以解决:
however, Scala 2 is known to not have a perfect support of GADTs, so sometimes compiler fails, even if it should work. I guess some compiler dev could figure out the exact case, but, interestingly, it can be worked around with:
sealed trait DSL[A]{ def run(): A = DSL.run(this) } object DSL { def run[A](fa:DSL[A]): A ={ fa match { case GetLength(something) => something.length case ShowResult(number) => s"the length is $number" } } } case class GetLength(something: String) extends DSL[Int] case class ShowResult(number: Int) extends DSL[String]我的疯狂猜测是,通用方法中的模式匹配在编译器中是一种特殊情况,当 A 固定时不会触发.我认为是这样,因为以下代码也可以工作:
My wild guess would be that pattern matching in a generic method is kind of a special case in a compiler, which is not triggered when A is fixed. I think that, because the following code also works:
sealed trait DSL[A]{ def run(): A = runMe(this) private def runMe[B](dsl: DSL[B]): B = { dsl match { case GetLength(something) => something.length case ShowResult(number) => s"the length is $number" } } }这同样会失败:
sealed trait DSL[A]{ def run(): A = { val fa: DSL[A] = this // make sure error is not related to special treatment of "this", this.type, etc fa match { case GetLength(something) => something.length case ShowResult(number) => s"the length is $number" } } } cmd4.sc:5: constructor cannot be instantiated to expected type; found : ammonite.$sess.cmd4.GetLength required: ammonite.$sess.cmd4.DSL[A] case GetLength(something) => ^ cmd4.sc:7: constructor cannot be instantiated to expected type; found : ammonite.$sess.cmd4.ShowResult required: ammonite.$sess.cmd4.DSL[A] case ShowResult(number) => ^ Compilation Failed换句话说,我怀疑类型参数会改变事物的评估方式:
In other words, I suspect that type parameter change how things are being evaluated:
- def runMe [B](dsl:DSL [B]):B 具有类型参数,因此 match 与 B 进行比较,在每种情况下,可以证明 B 的值是某种特定类型(Int,String)
- 在 def run:A 中,但是以某种方式阻止了编译器进行这种分析-恕我直言,这是一个bug,但这也许是某些晦涩功能的结果.
- def runMe[B](dsl: DSL[B]): B has a type parameter, so results of each case inside match are compared against B where for each case value of B can be proven to be some specific type (Int, String)
- in def run: A however compiler is somehow prevented from making such analysis - IMHO it is a bug, but perhaps it is a result of some obscure feature.
从我看到的相同错误中发现了Dotty,所以它要么是重复的错误,要么是类型级别检查器的限制(毕竟,所有GADT尚未广泛使用din Scala)-我建议将问题报告给Scala/Dotty团队让他们决定是什么.
From what I see the same error occurs in Dotty, so it is either duplicated bug or a limitation of a type-level checker (after all GADT aren't widely use din Scala, yet) - I would suggest reporting issue to Scala/Dotty team and letting them decide what it is.
更多推荐
在这种情况下,scala的类型检查如何工作?
发布评论