10

我想知道成员类型在 Scala 中是如何工作的,以及我应该如何关联类型。

一种方法是使关联类型成为类型参数。这种方法的优点是我可以规定类型的变化,并且我可以确定子类型不会改变类型。缺点是,我无法从函数中的类型推断类型参数。

第二种方法是使关联类型成为第二种类型的成员,它的问题是我无法对子类型的关联类型规定界限,因此,我不能在函数参数中使用该类型(当 x : X, X#T 可能与 xT 没有任何关系)

一个具体的例子是:

我有一个 DFA 的特征(可能没有 type 参数)

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]作为输入序列类型
  • 函数调用者不需要指定类型参数,都必须推断
  • 我希望使用具体的 DFA 类型调用该函数(但如果有一个解决方案,该函数没有 DFA 的类型参数,那没关系)
  • 字母表类型必须不受约束(即,必须有一个用于 Char 以及未知用户定义类的 DFA)
  • 具有不同字母类型的 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 的无效循环引用???(它是什么?))

我还删除了类型参数,创建了一个抽象类型 Sigma 并尝试在具体类中绑定该类型。runDFA 看起来像

def runDFA[D <: DFA, SQ <% Seq[D#Sigma]]( ... same as above

但这不可避免地会遇到诸如“类型不匹配:预期dfa.Sigma,得到D#Sigma”之类的问题

有任何想法吗?指针?

编辑:

由于答案表明没有简单的方法可以做到这一点,有人可以详细说明为什么这是不可能的以及必须改变什么才能奏效吗?

我希望 runDFA ro 成为一个自由函数(而不是方法)的原因是我想要其他类似的函数,如自动机最小化、常规语言操作、NFA 到 DFA 转换、语言分解等,并将所有这些都放在一个类中几乎违反了OO设计的任何原则。

4

3 回答 3

3

Scala 的类型推断有时还有很多不足之处。

有什么理由不能在 DFA 特征中包含该方法吗?

def run[SQ <% Seq[S]](seq: SQ)

如果您以后不需要 D 参数,您也可以尝试在没有它的情况下定义您的方法:

def runDFA[S, SQ <% Seq[S]](d: DFA[S])(seq: SQ) = ...
于 2009-02-26T22:23:02.883 回答
3

首先,您不需要参数化 SQ <% Seq[S]。将方法参数写为 Seq[S]。如果 SQ <% Seq[S] 那么它的任何实例都可以隐式转换为 Seq[S] (这就是 <% 的含义),因此当作为 Seq[S] 传递时,编译器将自动插入转换。

此外,Jorge 谈到 D 上的类型参数并使其成为 DFA 上的方法时所说的话。由于内部类在 Scala 中的工作方式,我强烈建议将 runDFA 放在 DFA 上。在依赖于路径的类型工作起作用之前,处理一些外部类的内部类可能会有点痛苦。

所以现在你有

trait DFA[S]{
  ...

  def runDFA(seq : Seq[S]) = ...
}

并且 runDFA 突然之间很容易推断类型参数:它没有任何类型参数。

于 2009-02-26T22:53:14.480 回答
0

关于两者有何不同的一些有用信息:

从无形指南

例如,如果没有类型参数,您将无法创建依赖类型

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,从而有效地使 getRepr 无用。直观的结论是类型参数可用作“输入”,类型成员可用作“输出”。

有关详细信息,请参阅无形指南

于 2017-04-27T09:53:41.980 回答