文档章节

谈谈Scala的语法糖

wiitht
 wiitht
发布于 2017/05/05 17:18
字数 1377
阅读 250
收藏 0

语法糖:指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。

通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

语法糖和其他编程思想一样重要,什么duck type,人本接口,最小接口,约定优于配置,广义来讲都是一些思想上的“语法糖“。

实际上从面向过程到面向对象也是一种语法糖,C语言可以通过它的指针、类型转换,结构实现面向对象的编程风格,但是C++更进一步的推广了这种风格,更加易用,不过到了C#把OO的风格发挥得淋漓尽致

1.符号语法糖

初学Scala看到那些稀奇古怪的符号(e.g.   <: , >: ,  <%  ,  =:= , <:< ,  <%<,  +T, -T ),总让人摸不着头脑,Scala创造这些语法糖究竟是要做甚?

1)上下界约束符号 <: 与 >:

这对符号个人觉得是里面最好理解的了,这对符号用于写范型类/函数时约束范型类型

先举个栗子:

def using[A <: Closeable, B](closeable: A) (getB: A => B): B =  
  try {   
    getB(closeable)  
  } finally {  
    closeable.close()   
  }  

例子中A <: Closeable(java.io.Cloaseable)的意思就是保证类型参数A是Closeable的子类(含本类),语法“A <: B"定义了B为A的上界;同理相反的A>:B的意思就是A是B的超类(含本类),定义了B为A的下界。

其实<: 和 >: 就等价于java范型编程中的 extends,super(PS: 说起来C#中只有where A:B形似的上界约束,怎么没有下界约束呢?求高人指教)

2) 协变与逆变符号+T, -T

“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。e.g. String => AnyRef

“逆变”则是指能够使用派生程度更小的类型。e.g. AnyRef => String

【+T】表示协变,【-T】表示逆变

3) view bounds(视界) 与 <%

<%的意思是“view bounds”(视界),它比<:适用的范围更广,除了所有的子类型,还允许隐式转换过去的类型

def method [A <% B](arglist): R = ...  
等价于
def method [A](arglist)(implicit viewAB: A => B): R = ...  
或等价于:
implicit def conver(a:A): B = …  
  
def method [A](arglist): R = ...  

<% 除了方法使用之外,class声明类型参数时也可使用:

scala> class A[T <% Int]  
defined class A  

但无法对trait的类型参数使用 <%,

scala> trait A[T <% Int]  
<console>:1: error: traits cannot have type parameters with context bounds `: ...' nor view bounds `<% ...'  

 

4) 广义类型约束符号 =:=, <:<,  <%<

这些被称为广义的类型约束。他们允许你从一个类型参数化的class或trait,进一步约束其类型参数之一。下面是一个例子:

case class Foo[A](a:A) { // 'A' can be substituted with any type  
    // getStringLength can only be used if this is a Foo[String]  
    def getStringLength(implicit evidence: A =:= String) = a.length  
}  

这个隐式的参数 evidence 由编译器提供,A =:=String表示证明A是String类型(PS:即使A可以隐式转换成String类型也不行),因此参数a就可以调用a.length 而编译器不会报错。

我们可以如下使用:

scala> Foo("blah").getStringLength  
res0: Int = 4  

 

一旦我们使用其他不能转换成String类型的参数,就会报错,如下:

scala> Foo(123).getStringLength  
<console>:10: error: Cannot prove that Int =:= String.  
              Foo(123).getStringLength 

 

scala> implicit def charSeq2String(s: Seq[Char]) = s.mkString  
charSeq2String: (s: Seq[Char])String  
  
scala> Foo(Seq[Char]('a','b','c')).getStringLength  
<console>:11: error: Cannot prove that Seq[Char] =:= String.  
              Foo(Seq[Char]('a','b','c')).getStringLength  

 

<:< 和 <%< 使用类似, 有细微不同:

  • A =:= B 表示 A 必须是 B 类型
  • A <:< B 表示 A 必须是B的子类型 (类似于简单类型约束 <:)
  • A <%< B 表示 A 必须是可视化为 B类型, 可能通过隐式转换 (类似与简单类型约束 <%)

5) 传名调用(call-by-name)符号: => type

传名调用 (Call by name)

在“传名调用”求值中,根本就不求值给函数的实际参数 — 而是使用避免捕获代换把函数的实际参数直接代换入函数体内。如果实际参数在函数的求值中未被用到,则它永不被求值;如果这个实际参数使用多次,则它每次都被重新求值。

传名调用求值超过传值调用求值的优点是传名调用求值在一个值存在的时候总是生成这个值,而传名调用可能不终止如果这个函数的实际参数是求值这个函数所不需要的不终止计算。反过来说,在函数的实际参数会用到的时候传名调用就非常慢了,这是因为实践中几乎总是要使用如 thunk 这样的机制。

传需求调用 (Call by need)

“传需求调用”是传名调用的记忆化版本,如果“函数的实际参数被求值了”,这个值被存储起来已备后续使用。在“纯”(无副作用)设置下,这产生同传名调用一样的结果;当函数实际参数被使用两次或更多次的时候,传需求调用总是更快。

Scala中call by name使用:

object TargetTest2 extends Application {  
  def loop(body: => Unit): LoopUnlessCond =  
    new LoopUnlessCond(body)  
  protected class LoopUnlessCond(body: => Unit) {  
    def unless(cond: => Boolean) {  
      body  
      if (!cond) unless(cond)  
    }  
  }  
  var i = 10  
  loop {  
    println("i = " + i)  
    i -= 1  
  } unless (i == 0)  
}  
上面的程序运行结果是
i = 10  
i = 9  
i = 8  
i = 7  
i = 6  
i = 5  
i = 4  
i = 3  
i = 2  
i = 1  

 

© 著作权归作者所有

上一篇: 谈谈Scala的特性
下一篇: SSO单点登录过程
wiitht
粉丝 4
博文 158
码字总数 113941
作品 0
深圳
架构师
私信 提问
关于scala搞出的新概念的语法糖

对于scala搞那么多语法糖和新概念真是又爱又恨。爱的是scala引入了java一直没有的lambda特性,这对于使用高阶函数抽象来处理集合数据非常有爱(spark简洁的RDD处理得益于此)。恨的是scala搞...

中成才
2015/11/15
184
0
Scala入门之工具篇

我初次接触Scala时,由于对Scala相关的工具不够熟悉,学习的效率低下。所以本文主要介绍Scala编程所必备的工具。一般而言,我们接触一门编程语言,都需要接触这门语言的编译器、REPL、构建工...

碎镜
2017/11/29
0
0
使用implicit语法糖给类型对象动态添加方法

在动态语言中,比如js, ruby可以在运行时给某个数据类型添加方法或属性,但在静态语言(比如java)就无法做到,但scala提供的implicit语法糖可以达到这种效果。 比如我们希望给java日期类型j...

mj4738
2011/11/07
726
2
Scala类型系统——高级类类型(higher-kinded types)

高级类类型就是使用其他类型构造成为一个新的类型,因此也称为 类型构造器(type constructors)。它的语法和高阶函数(higher-order functions)相似,高阶函数就是将其它函数作为参数的函数;高...

Barudisshu
2016/06/13
948
0
Kotlin Type? vs Scala Option

本文由 KnewHow 发表在 ScalaCool 团队博客。 最近阅读一些关于 Kotlin 类型系统方面的书,发现 Kotlin 的类型系统针对 有着独特的设计哲学。在 Java 或者其它编程语言中,经常会出现 ,而导...

ScalaCool
01/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Flink Graph生成及Hash生成分析

222

MrPei
6分钟前
1
0
[译]Android Activity 和 Fragment 状态保存与恢复的最佳实践

https://blog.csdn.net/growing_tree/article/details/53759564 https://blog.csdn.net/u013588712/article/details/54691791...

shzwork
7分钟前
1
0
调用第三方快递鸟物流单号查询接口API代码示例

最近进行网站后台开发,需要实现物流的即时查询,发现之前集成的 快递100物流查询 API ——【PHP 快递查询源码资源】 已经不能正常使用了; 为了方便以后的业务需求,经过比较,最后选择使用...

程序的小猿
14分钟前
2
0
java Poi 操作执行excel 文件中函数问题

poi 读取excel 文件,当excel 有函数时,poi直接读取返回的是excel 函数,并不能返回函数计算结果: 解决步骤: sheet.setForceFormulaRecalculation(true); 判断该列格式是否为...

早a
22分钟前
3
0
js模拟实现输入框input事件

直接修改value值是无法触发对应元素的事件的。 通过发送输入框input事件了, 可以触发。 这里简单封装了一个方法. window.inputValue = function (dom, st) { var evt = new InputEvent('i...

開援带碼
23分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部