Scala School 笔记(五)--高级类型
2014-02-14 17:04
253 查看
好像完全看不懂
有时候,你并不需要指定一个类型是等/子/超于另一个类,你可以通过转换这个类来伪装这种关联关系。一个视界指定一个类型可以被“看作是”另一个类型。这对对象的只读操作是很有用的。
隐 函数允许类型自动转换。更确切地说,在隐式函数可以帮助满足类型推断时,它们允许按需的函数应用。例如:
视界,就像类型边界,要求对给定的类型存在这样一个函数。您可以使用
这是说 A 必须“可被视”为 Int 。让我们试试。
方法可以通过隐含参数执行更复杂的类型限制。例如,
定义了一个隐含的
如果你调用
在没有设定陌生的对象为
类似地,根据之前的隐式转换,我们可以放松约束为可视性:
在Scala标准库中,视图主要用于实现集合的通用函数。例如“min”函数(在 Seq[] 上)就使用了这种技术:
其主要优点是:
集合中的元素并不是必须实现 Ordered 特质,但 Ordered 的使用仍然可以执行静态类型检查。
无需任何额外的库支持,你也可以定义自己的排序:
作为旁注,标准库中有视图来将 Ordered 转换为 Ordering (反之亦然)。
Scala2.8引入了一种串联和访问隐式参数的快捷方式。
隐式值可能会通过 implicitly 被访问
相结合后往往会使用更少的代码,尤其是串联视图的时候。
Scala可以对“更高阶”的类型进行抽象。例如,假设您需要用几种类型的容器处理几种类型的数据。你可能定义了一个
这类似与函数柯里化。例如,尽管“一元类型”有类似
注意:*Container*是参数化类型的多态(“容器类型”)。
如果我们结合隐式转换implicits使用容器,我们会得到“特设的”多态性:即对容器写泛型函数的能力。
通常有必要来访问一个(泛型)特质的具体子类。例如,想象你有一些泛型特质,但需要可以与它的某一子类进行比较。
然而,现在比较方法是必须的了
因此,我们不能访问具体子类型,例如:
编译失败,因为我们对 Container 指定了Ordered特质,而不是对特定子类型指定的。
为了调和这一点,我们改用F-界的多态性。
奇怪的类型!但可以看到怎样对 A 实现了Ordered参数化,它本身就是 Container[A]
所以,现在
他们是有序的了:
鉴于他们都是 Container[_] 的子类型,我们可以定义另一个子类并创建 Container[_] 的一个混合列表:
注意结果类型是怎样成为 YourContainer 和 MyContainer 类型确定的下界。这是类型推断的工作。有趣的是,这种类型甚至不需要是有意义的,它只是提供了一个合乎逻辑的最大下界为列表的统一类型。如果现在我们尝试使用 Ordered 会发生什么?
对统一的类型 Ordered[]不存在了。太糟糕了。
Scala 支持 结构类型 structural types — 类型需求由接口 构造 表示,而不是由具体的类型表示。
这可能在很多场景都是相当不错的,但这个实现中使用了反射,所以要注意性能!
在特质中,你可以让类型成员保持抽象。
在做依赖注入等情况下,这往往是一个有用的技巧。
您可以使用hash操作符来引用一个抽象类型的变量:
正如我们所知道的,类型信息在编译的时候会因为 擦除 而丢失。 Scala的 清单(Manifests) 功能,使我们能够选择性地恢复类型信息。清单提供了一个隐含值,根据需要由编译器生成。
参见: https://github.com/twitter/finagle
一个服务可以通过过滤器对请求进行身份验证。
注意底层服务是如何需要对请求进行身份验证的,而且还是静态验证。因此,过滤器可以被看作是服务转换器。
许多过滤器可以被组合在一起:
享用安全的类型吧!
--------------待续
视界(“类型类”)
有时候,你并不需要指定一个类型是等/子/超于另一个类,你可以通过转换这个类来伪装这种关联关系。一个视界指定一个类型可以被“看作是”另一个类型。这对对象的只读操作是很有用的。隐 函数允许类型自动转换。更确切地说,在隐式函数可以帮助满足类型推断时,它们允许按需的函数应用。例如:
scala> implicit def strToInt(x: String) = x.toInt strToInt: (x: String)Int scala> "123" res0: java.lang.String = 123 scala> val y: Int = "123" y: Int = 123 scala> math.max("123", 111) res1: Int = 123
视界,就像类型边界,要求对给定的类型存在这样一个函数。您可以使用
<%指定类型限制,例如:
scala> class Container[A <% Int] { def addIt(x: A) = 123 + x } defined class Container
这是说 A 必须“可被视”为 Int 。让我们试试。
scala> (new Container[String]).addIt("123") res11: Int = 246 scala> (new Container[Int]).addIt(123) res12: Int = 246 scala> (new Container[Float]).addIt(123.2F) <console>:8: error: could not find implicit value for evidence parameter of type (Float) => Int (new Container[Float]).addIt(123.2) ^
其他类型限制
方法可以通过隐含参数执行更复杂的类型限制。例如,List支持对数字内容执行
sum,但对其他内容却不行。可是Scala的数字类型并不都共享一个超类,所以我们不能使用
T <: Number。相反,要使之能工作,Scala的math库对适当的类型T
定义了一个隐含的
Numeric[T]。 然后在
List定义中使用它:
sum[B >: A](implicit num: Numeric[B]): B
如果你调用
List(1,2).sum(),你并不需要传入一个 num 参数;它是隐式设置的。但如果你调用
List("whoop").sum(),它会抱怨无法设置
num。
在没有设定陌生的对象为
Numeric的时候,方法可能会要求某种特定类型的“证据”。这时可以使用以下类型-关系运算符:
A =:= B | A 必须和 B相等 |
A <:< B | A 必须是 B的子类 |
A <%< B | A 必须可以被看做是 B |
scala> class Container[A](value: A) { def addIt(implicit evidence: A =:= Int) = 123 + value } defined class Container scala> (new Container(123)).addIt res11: Int = 246 scala> (new Container("123")).addIt <console>:10: error: could not find implicit value for parameter evidence: =:=[java.lang.String,Int]
类似地,根据之前的隐式转换,我们可以放松约束为可视性:
scala> class Container[A](value: A) { def addIt(implicit evidence: A <%< Int) = 123 + value } defined class Container scala> (new Container("123")).addIt res15: Int = 246
使用视图进行泛型编程
在Scala标准库中,视图主要用于实现集合的通用函数。例如“min”函数(在 Seq[] 上)就使用了这种技术:def min[B >: A](implicit cmp: Ordering[B]): A = { if (isEmpty) throw new UnsupportedOperationException("empty.min") reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y) }
其主要优点是:
集合中的元素并不是必须实现 Ordered 特质,但 Ordered 的使用仍然可以执行静态类型检查。
无需任何额外的库支持,你也可以定义自己的排序:
scala> List(1,2,3,4).min res0: Int = 1 scala> List(1,2,3,4).min(new Ordering[Int] { def compare(a: Int, b: Int) = b compare a }) res3: Int = 4
作为旁注,标准库中有视图来将 Ordered 转换为 Ordering (反之亦然)。
trait LowPriorityOrderingImplicits { implicit def ordered[A <: Ordered[A]]: Ordering[A] = new Ordering[A] { def compare(x: A, y: A) = x.compare(y) } }
上下文边界和implicitly[]
Scala2.8引入了一种串联和访问隐式参数的快捷方式。scala> def foo[A](implicit x: Ordered[A]) {} foo: [A](implicit x: Ordered[A])Unit scala> def foo[A : Ordered] {} foo: [A](implicit evidence$1: Ordered[A])Unit
隐式值可能会通过 implicitly 被访问
scala> implicitly[Ordering[Int]] res37: Ordering[Int] = scala.math.Ordering$Int$@3a9291cf
相结合后往往会使用更少的代码,尤其是串联视图的时候。
更高级多态性类型 和 特设多态性
Scala可以对“更高阶”的类型进行抽象。例如,假设您需要用几种类型的容器处理几种类型的数据。你可能定义了一个Container的接口,它可以被实现为几种类型的容器:
Option、
List等。你要定义可以使用这些容器里的值的接口,但不想确定值的类型。
这类似与函数柯里化。例如,尽管“一元类型”有类似
List[A]的构造函数,这意味着我们必须满足一个“级别”的类型变量来产生一个具体的类型(就像一个没有柯里化的函数需要只提供一个参数列表来被调用),更高阶的类型需要更多。
scala> trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A } scala> val container = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head } container: java.lang.Object with Container[List] = $anon$1@7c8e3f75 scala> container.put("hey") res24: List[java.lang.String] = List(hey) scala> container.put(123) res25: List[Int] = List(123)
注意:*Container*是参数化类型的多态(“容器类型”)。
如果我们结合隐式转换implicits使用容器,我们会得到“特设的”多态性:即对容器写泛型函数的能力。
scala> trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A } scala> implicit val listContainer = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head } scala> implicit val optionContainer = new Container[Some] { def put[A](x: A) = Some(x); def get[A](m: Some[A]) = m.get } scala> def tupleize[M[_]: Container, A, B](fst: M[A], snd: M[B]) = { | val c = implicitly[Container[M]] | c.put(c.get(fst), c.get(snd)) | } tupleize: [M[_],A,B](fst: M[A],snd: M[B])(implicit evidence$1: Container[M])M[(A, B)] scala> tupleize(Some(1), Some(2)) res33: Some[(Int, Int)] = Some((1,2)) scala> tupleize(List(1), List(2)) res34: List[(Int, Int)] = List((1,2))
F-界多态性
通常有必要来访问一个(泛型)特质的具体子类。例如,想象你有一些泛型特质,但需要可以与它的某一子类进行比较。trait Container extends Ordered[Container]
然而,现在比较方法是必须的了
def compare(that: Container): Int
因此,我们不能访问具体子类型,例如:
class MyContainer extends Container { def compare(that: MyContainer): Int }
编译失败,因为我们对 Container 指定了Ordered特质,而不是对特定子类型指定的。
为了调和这一点,我们改用F-界的多态性。
trait Container[A <: Container[A]] extends Ordered[A]
奇怪的类型!但可以看到怎样对 A 实现了Ordered参数化,它本身就是 Container[A]
所以,现在
class MyContainer extends Container[MyContainer] { def compare(that: MyContainer) = 0 }
他们是有序的了:
scala> List(new MyContainer, new MyContainer, new MyContainer) res3: List[MyContainer] = List(MyContainer@30f02a6d, MyContainer@67717334, MyContainer@49428ffa) scala> List(new MyContainer, new MyContainer, new MyContainer).min res4: MyContainer = MyContainer@33dfeb30
鉴于他们都是 Container[_] 的子类型,我们可以定义另一个子类并创建 Container[_] 的一个混合列表:
scala> class YourContainer extends Container[YourContainer] { def compare(that: YourContainer) = 0 } defined class YourContainer scala> List(new MyContainer, new MyContainer, new MyContainer, new YourContainer) res2: List[Container[_ >: YourContainer with MyContainer <: Container[_ >: YourContainer with MyContainer <: ScalaObject]]] = List(MyContainer@3be5d207, MyContainer@6d3fe849, MyContainer@7eab48a7, YourContainer@1f2f0ce9)
注意结果类型是怎样成为 YourContainer 和 MyContainer 类型确定的下界。这是类型推断的工作。有趣的是,这种类型甚至不需要是有意义的,它只是提供了一个合乎逻辑的最大下界为列表的统一类型。如果现在我们尝试使用 Ordered 会发生什么?
(new MyContainer, new MyContainer, new MyContainer, new YourContainer).min <console>:9: error: could not find implicit value for parameter cmp: Ordering[Container[_ >: YourContainer with MyContainer <: Container[_ >: YourContainer with MyContainer <: ScalaObject]]]
对统一的类型 Ordered[]不存在了。太糟糕了。
结构类型
Scala 支持 结构类型 structural types — 类型需求由接口 构造 表示,而不是由具体的类型表示。scala> def foo(x: { def get: Int }) = 123 + x.get foo: (x: AnyRef{def get: Int})Int scala> foo(new { def get = 10 }) res0: Int = 133
这可能在很多场景都是相当不错的,但这个实现中使用了反射,所以要注意性能!
抽象类型成员
在特质中,你可以让类型成员保持抽象。scala> trait Foo { type A; val x: A; def getX: A = x } defined trait Foo scala> (new Foo { type A = Int; val x = 123 }).getX res3: Int = 123 scala> (new Foo { type A = String; val x = "hey" }).getX res4: java.lang.String = hey
在做依赖注入等情况下,这往往是一个有用的技巧。
您可以使用hash操作符来引用一个抽象类型的变量:
scala> trait Foo[M[_]] { type t[A] = M[A] } defined trait Foo scala> val x: Foo[List]#t[Int] = List(1) x: List[Int] = List(1)
类型擦除和清单
正如我们所知道的,类型信息在编译的时候会因为 擦除 而丢失。 Scala的 清单(Manifests) 功能,使我们能够选择性地恢复类型信息。清单提供了一个隐含值,根据需要由编译器生成。scala> class MakeFoo[A](implicit manifest: Manifest[A]) { def make: A = manifest.erasure.newInstance.asInstanceOf[A] } scala> (new MakeFoo[String]).make res10: String = ""
案例分析: Finagle
参见: https://github.com/twitter/finagle trait Service[-Req, +Rep] extends (Req => Future[Rep]) trait Filter[-ReqIn, +RepOut, +ReqOut, -RepIn] extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut]) { def andThen[Req2, Rep2](next: Filter[ReqOut, RepIn, Req2, Rep2]) = new Filter[ReqIn, RepOut, Req2, Rep2] { def apply(request: ReqIn, service: Service[Req2, Rep2]) = { Filter.this.apply(request, new Service[ReqOut, RepIn] { def apply(request: ReqOut): Future[RepIn] = next(request, service) override def release() = service.release() override def isAvailable = service.isAvailable }) } } def andThen(service: Service[ReqOut, RepIn]) = new Service[ReqIn, RepOut] { private[this] val refcounted = new RefcountedService(service) def apply(request: ReqIn) = Filter.this.apply(request, refcounted) override def release() = refcounted.release() override def isAvailable = refcounted.isAvailable } }
一个服务可以通过过滤器对请求进行身份验证。
trait RequestWithCredentials extends Request { def credentials: Credentials } class CredentialsFilter(credentialsParser: CredentialsParser) extends Filter[Request, Response, RequestWithCredentials, Response] { def apply(request: Request, service: Service[RequestWithCredentials, Response]): Future[Response] = { val requestWithCredentials = new RequestWrapper with RequestWithCredentials { val underlying = request val credentials = credentialsParser(request) getOrElse NullCredentials } service(requestWithCredentials) } }
注意底层服务是如何需要对请求进行身份验证的,而且还是静态验证。因此,过滤器可以被看作是服务转换器。
许多过滤器可以被组合在一起:
val upFilter = logTransaction andThen handleExceptions andThen extractCredentials andThen homeUser andThen authenticate andThen route
享用安全的类型吧!
--------------待续
相关文章推荐
- Scala School 笔记(四)--类型和多态基础
- 快学Scala学习笔记及习题解答(17-18类型参数与高级类型)
- JavaScript 高级程序设计 第5章引用类型 笔记
- JS高级程序设计笔记(五):引用类型
- Programming In Scala笔记-第十九章、类型参数,协变逆变,上界下界
- Scala入门到精通——第二十二节 高级类型 (一)
- 【scala 笔记(2)】 控制结构 -- 高级for循环和for推导式
- 【Scala学习笔记】类型参数化数组
- Scala学习笔记(三) - 基础类型
- JavaScript高级程序设计(第3版)学习笔记3 js简单数据类型
- scala快速学习笔记(一):变量函数,操作符,基本类型
- Google KML 起步教程笔记(二)高级 KML 文档与MIME 类型
- JS高级程序设计笔记(一)-数据类型
- Scala学习笔记--隐式类型和隐式转换
- scala学习笔记-类型参数中协变(+)、逆变(-)、类型上界(<:)和类型下界(>:)的使用
- scala一些高级类型
- scala 高级类型
- Scala入门到精通——第二十三节 高级类型 (二)
- Programming In Scala笔记-第十七章、Scala中的集合类型
- Java高级语法笔记-枚举类型