斯卡拉:请解释所涉及的仿制药

时间:2014-12-18 11:35:57

标签: scala playframework

有人可以解释播放框架

中以下代码中涉及的泛型
class AuthenticatedRequest[A, U](val user: U, request: Request[A]) extends WrappedRequest[A](request)

class AuthenticatedBuilder[U](userinfo: RequestHeader => Option[U],
        onUnauthorized: RequestHeader => Result = _ => Unauthorized(views.html.defaultpages.unauthorized()))
          extends ActionBuilder[({ type R[A] = AuthenticatedRequest[A, U] })#R]

ActionBuilder实际上有类型R [A],它正在被重新分配,这是我理解的。请解释语法

的复杂性

1 个答案:

答案 0 :(得分:5)

令你困惑的是一个"类型lambda"。如果你搜索" scala type lambda",你会发现很多描述和解释。参见例如here,我从中吸取了很多灵感。 (谢谢Bartosz Witkowski!)

为了简单地描述它,您可以将其视为向类型构造函数提供默认参数的一种方法。我知道,是吗?

让我们打破这种局面。如果我们有......

trait Unwrapper[A,W[_]] {
  /* should throw an Exception if we cannot unwrap */
  def unwrap( wrapped : W[A] ) : A 
}

您可以轻松定义OptionUnwrapper:

class OptionUnwrapper[A] extends Unwrapper[A,Option] {
  def unwrap( wrapped : Option[A] ) : A = wrapped.get
}

但是如果我们想要为非常相似的Either类定义一个unwrapper,它需要两个类型参数[A,B]。和Option一样,often used as a return value for things that might fail也是{{3}},但您可能希望保留有关失败的信息。按照惯例,"成功"导致一个包含B的Right对象,而失败产生一个包含A的Left对象。让我们创建一个EitherUnwrapper,所以我们有一个与Option共同的接口来解包这些可用的结果。 (可能有用!)

class EitherUnwrapper[A,B] extends Unwrapper[B,Either] { // unwrap to a successful result of type B
  def unwrap( wrapped : Either[A,B] ) : B = wrapped match {
     case Right( b ) => b // we ignore the left case, allowing a MatchError
  }
}

这在概念上很好,但它没有编译!为什么不?因为Unwrapper的第二个参数是W [_],这是一个只接受一个参数的类型。我们怎样才能适应"这两种类型的构造函数是一个参数类型?如果我们需要一个普通函数或具有较少参数的构造函数的版本,我们可能会提供默认参数。这正是我们所做的。

class EitherUnwrapper[A,B] extends Unwrapper[B,({type L[C] = Either[A,C]})#L] { 
  def unwrap( wrapped : Either[A,B] ) : B = wrapped match {
     case Right( b ) => b 
  }
}

类型别名部分

type L[C] = Either[A,C]

通过提供A作为默认的第一个类型参数,将其转换为只需要一个类型参数而不是两个类型的类型。但遗憾的是,scala并不允许您在任何地方定义类型别名:它们必须存在于类,特征或对象中。但是如果在封闭范围内定义特征,则可能无法访问类型A所需的默认值!因此,诀窍是在定义A的位置定义一个一次性的内部类,就在需要新类型的地方。

对于结构类型,一组花括号可以(取决于上下文)被解释为scala中的类型定义。例如......

def destroy( rsrc : { def close() } ) = rsrc.close()

...花括号定义了一个结构类型,意味着任何具有close()函数的对象。结构类型还可以包括类型别名。

所以{ type L[C] = Either[A,C] }只是包含类型别名L [C]的任何对象的类型。要在Scala中从封闭类型(而不是封闭实例)中提取内部类型,我们必须使用类型投影而不是点。类型投影的语法是EnclosingType#InnerType。所以,我们有{ type L[C] = Either[A,C] }#L。由于我不能理解的原因,Scala编译器对此感到困惑,但是如果我们将类型定义放在括号中,那么一切都有效,所以我们有({ type L[C] = Either[A,C] })#L

在您的问题中,这与({ type R[A] = AuthenticatedRequest[A, U] })#R非常相似。 ActionBuilder需要使用带有一个参数的类型进行参数化。 AuthenticatedRequest有两个参数。为了使AuthenticatedRequest适应适合ActionBuilder的类型,U作为lambda类型的默认参数提供。