Jef*_*eff 6 scala type-inference module functor inner-classes
我想使用对象实例作为模块/仿函数,或多或少,如下所示:
abstract class Lattice[E] extends Set[E] {
val minimum: E
val maximum: E
def meet(x: E, y: E): E
def join(x: E, y: E): E
def neg(x: E): E
}
class Calculus[E](val lat: Lattice[E]) {
abstract class Expr
case class Var(name: String) extends Expr {...}
case class Val(value: E) extends Expr {...}
case class Neg(e1: Expr) extends Expr {...}
case class Cnj(e1: Expr, e2: Expr) extends Expr {...}
case class Dsj(e1: Expr, e2: Expr) extends Expr {...}
}
Run Code Online (Sandbox Code Playgroud)
这样我就可以为每个晶格创建一个不同的微积分实例(我将执行的操作需要其中的信息是晶格的最大值和最小值).我希望能够混合相同微积分的表达式,但不允许混合不同表达式的表达式.到现在为止还挺好.我可以创建我的微积分实例,但问题是我不能在其他操作它们的类中编写函数.
例如,我正在尝试创建一个解析器来读取文件中的表达式并返回它们; 我也试图编写一个随机表达式生成器,用于我的ScalaCheck测试.事实证明,每次函数生成Expr对象时,我都无法在函数外部使用它.即使我创建了Calculus实例并将其作为参数传递给将依次生成Expr对象的函数,函数的返回也不会被识别为与函数外部创建的对象类型相同.
也许我的英语不够清楚,让我尝试一下我想做的玩具示例(不是真正的ScalaCheck发生器,但足够接近).
def genRndExpr[E](c: Calculus[E], level: Int): Calculus[E]#Expr = {
if (level > MAX_LEVEL) {
val select = util.Random.nextInt(2)
select match {
case 0 => genRndVar(c)
case 1 => genRndVal(c)
}
}
else {
val select = util.Random.nextInt(3)
select match {
case 0 => new c.Neg(genRndExpr(c, level+1))
case 1 => new c.Dsj(genRndExpr(c, level+1), genRndExpr(c, level+1))
case 2 => new c.Cnj(genRndExpr(c, level+1), genRndExpr(c, level+1))
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我尝试编译上面的代码,我会得到很多
error: type mismatch;
found : plg.mvfml.Calculus[E]#Expr
required: c.Expr
case 0 => new c.Neg(genRndExpr(c, level+1))
如果我尝试做类似的事情,也会发生同样的事情:
val boolCalc = new Calculus(Bool)
val e1: boolCalc.Expr = genRndExpr(boolCalc)
Run Code Online (Sandbox Code Playgroud)
请注意,生成器本身并不重要,但我需要在系统的其余部分做很多类似的事情(即创建和操作微积分实例表达式).
难道我做错了什么?有可能做我想做的事吗?
非常需要和赞赏这方面的帮助.非常感谢提前.
收到Apocalisp的答案并尝试后.
非常感谢您的回答,但仍有一些问题.建议的解决方案是将函数的签名更改为:
def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
Run Code Online (Sandbox Code Playgroud)
我更改了所涉及的所有函数的签名:getRndExpr,getRndVal和getRndVar.我在调用这些函数时得到了相同的错误消息,并收到以下错误消息:
error: inferred type arguments [Nothing,C] do not conform to method genRndVar's
type parameter bounds [E,C <: plg.mvfml.Calculus[E]]
case 0 => genRndVar(c)
由于编译器似乎无法找出正确的类型,我将所有函数调用更改为如下所示:
case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
Run Code Online (Sandbox Code Playgroud)
在此之后,在前两个函数调用(genRndVal和genRndVar)上没有编译错误,但在以下3个调用(对genRndExpr的递归调用)中,函数的返回用于构建新的Expr对象,我得到了以下错误:
error: type mismatch;
found : C#Expr
required: c.Expr
case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
所以,再一次,我被困住了.任何帮助将不胜感激.
问题是 Scala 无法统一Calculus[E]#Expr和两种类型Calculus[E]#Expr。
但这些对你来说看起来是一样的,对吧?好吧,考虑一下您可能对某种 type 有两个不同的演算E,每个演算都有自己的Expr类型。而且你不会想混合这两种表达方式。
您需要以某种方式约束类型,使返回类型Expr与参数Expr的内部类型相同Calculus。你需要做的是这样的:
def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
558 次 |
| 最近记录: |