我想知道成员类型在Scala中的工作方式,以及如何关联类型。
一种方法是使关联类型成为类型参数。这种方法的优点是我可以规定类型的方差,我可以确定子类型不会改变类型。缺点是,我无法从函数中的类型推断出类型参数。
第二种方法是使关联类型成为第二种类型的成员,其问题是我不能在子类型的相关类型上规定边界,因此,我不能在函数参数中使用类型(当x:X,X#T可能与xT没有任何关系时
一个具体的例子是:
我有DFA的特征(可能没有类型参数)
trait DFA[S] { /* S is the type of the symbols in the alphabet */
trait State { def next(x : S); }
/* final type Sigma = S */
}
我想创建一个函数来在输入序列上运行这个DFA,我想要
<% Seq[alphabet-type-of-the-dfa]
作为输入序列类型我试过了:
def runDFA[S, D <: DFA[S], SQ <% Seq[S]](d : D)(seq : SQ) = ....
这是有效的,除了这里没有推断出类型S,所以我必须在每个调用站点上编写整个类型参数列表。
def runDFA[D <: DFA[S] forSome { type S }, SQ <% Seq[D#Sigma]]( ... same as above
这不起作用(类型D ???的无效循环引用(它是什么?))
我还删除了type参数,创建了一个抽象类型Sigma并尝试在具体类中绑定该类型。 runDFA看起来像
def runDFA[D <: DFA, SQ <% Seq[D#Sigma]]( ... same as above
但这不可避免地遇到“类型不匹配:预期dfa.Sigma
,得到D#Sigma
”等问题
有什么想法吗?指针?
编辑:
由于答案表明没有简单的方法可以做到这一点,有人可以详细说明为什么这是不可能的,什么必须改变才能起作用?
我想要runDFA ro成为自由函数(不是方法)的原因是我想要其他类似的函数,例如自动机最小化,常规语言操作,NFA到DFA转换,语言分解等等,并拥有所有这些在一个类中,几乎与任何OO设计原则相反。
答案 0 :(得分:3)
Scala的类型推断有时候还有很多不足之处。
你有什么理由不能在你的DFA特质中使用这个方法吗?
def run[SQ <% Seq[S]](seq: SQ)
如果您以后不需要D param,您也可以尝试在没有它的情况下定义您的方法:
def runDFA[S, SQ <% Seq[S]](d: DFA[S])(seq: SQ) = ...
答案 1 :(得分:3)
首先,您不需要参数化SQ&lt;%Seq [S]。将方法参数写为Seq [S]。如果SQ&lt;%Seq [S]那么它的任何实例都可以隐式转换为Seq [S](那是&lt;%表示),所以当作为Seq [S]传递时,编译器将自动插入转换。
此外,Jorge所说的关于D上的类型参数并使其成为DFA保持的方法。由于内部类在Scala中的工作方式,我会强烈建议将runDFA放在DFA上。直到路径依赖类型的东西工作,处理一些外部类的内部类可能有点痛苦。
所以现在你有了
trait DFA[S]{
...
def runDFA(seq : Seq[S]) = ...
}
并且runDFA突然很容易推断出类型参数:它没有任何参数。
答案 2 :(得分:0)
关于两者如何不同的一些有用信息:
来自无形guide:
如果没有类型参数,则无法创建依赖类型,例如
trait Generic[A] {
type Repr
def to(value: A): Repr
def from(value: Repr): A
}
import shapeless.Generic
def getRepr[A](value: A)(implicit gen: Generic[A]) =
gen.to(value)
此处to
返回的类型取决于输入类型A
(因为提供的隐式取决于A
):
case class Vec(x: Int, y: Int)
case class Rect(origin: Vec, size: Vec)
getRepr(Vec(1, 2))
// res1: shapeless.::[Int,shapeless.::[Int,shapeless.HNil]] = 1 :: 2 ::
HNil
getRepr(Rect(Vec(0, 0), Vec(5, 5)))
// res2: shapeless.::[Vec,shapeless.::[Vec,shapeless.HNil]] = Vec(0,0)
:: Vec(5,5) :: HNil
没有类型成员,这是不可能的:
trait Generic2[A, Repr]
def getRepr2[A, R](value: A)(implicit generic: Generic2[A, R]): R =
???
我们必须将所需的Repr值传递给getRepr作为a 类型参数,有效地使getRepr无用。直觉 从中可以看出,类型参数可用作“输入”和 类型成员可用作“输出”。
请参阅无形guide了解详情。