Scalaz(1)- 基础篇:隐式转换解析策略-Implicit resolution
在正式进入scalaz讨论前我们需要理顺一些基础的scalaz结构组成概念和技巧。scalaz是由即兴多态(ad-hoc polymorphism)类型(typeclass)组成。scalaz typeclass在scala中的应用有赖于scala compiler的一项特别功能:隐式转换(implicit conversion),使程序表述更精简。由于隐式转换是一项compiler功能,在程序编译(compile)的时候是由compiler来进行类型转换代码的产生和替代的。
让我们先了解一下作用域(scope)和绑定(binding)。这两样都是在编译程序时compiler需要解决的问题。所谓作用域解析(scope resolution)就是要确定一个绑定在一个作用域里是可视的,否则程序无法通过编译。
作用域就是一个绑定在一个程序范围内的可视型。作用域可以是某个类的内部或者是某个方法或函数的内部,基本上用{}就可以创造一个新的作用域了。在scala作用域可以是多层的,一个域可以存在于另一个作用域内。外部域的绑定在内部域内是可视的,反之则不然:
class Foo(x: Int) {
def temp = {
val y = x + //x是本地域外的一个绑定
}
}
在以上的例子里x在temp{}内是可视的。一个作用域内的绑定可以屏蔽(shadow)外域定义的绑定:
class Foo(x: Int) {
def temp = {
val x = //本地域绑定。屏蔽了外域的x
val y = x + //y=1,x是本地域的一个绑定
}
}
绑定屏蔽是分优先次序如下:
1、本地声明、定义或者透过继承又或者在同一源代码文件内的package定义的绑定最优先
2、明式申明的import如:import obj.Foo 所定义的绑定次优先
3、通配符式的import如:import obj._ 所定义的绑定再次之
4、同一package但处于不同源代码文件内的绑定最次优先
我们用个例子来示范scope binding的优先顺序:
package test; // This object contains the bindings/scope tests
object Test { def main(arg : Array[String]) : Unit = {
testSamePackage()
testWildcardImport()
testExplicitImport()
testInlineDefinition()
} // This looks for a binding 'x' within the same package (test) as this scope.
def testSamePackage() {
println(x) // 在另外文件的test package. prints: Externally bound x object in package test
} // This defines a new scope with an 'x' binding that we can import with a wildcard.
object Wildcard {
def x = "Wildcard Import x"
} // This function will print the value in the binding 'x' after importing from the Wildcard object
// using a wildcard import.
def testWildcardImport() {
import Wildcard._
println(x) // prints: Wildcard Import x
} // This defines another binding of 'x' that we can import explicitly.
object Explicit {
def x = "Explicit Import x"
} def testExplicitImport() {
import Explicit.x
import Wildcard._
println(x) // .x优先于._ prints: Explicit Import x
} // This defines an inline binding for x. Note that with all the imports, there are no ambiguous naming conflicts.
def testInlineDefinition() {
val x = "Inline definition x" //即使写在最前,本地binding x还是最优先
import Explicit.x
import Wildcard._
println(x) // prints: Inline definition x
}
}
scala compiler 在编译程序时会根据情况自动进行隐式转换,即代码替代。在两种情况下scala会进行隐形转换:
1、在期待一个类型的地方发现了另外一个类型:
package learn.scalaz
object ab {
class A
class B
implicit def bToA(x: B): A = new A
}
object testApp extends App {
import ab._
val a: A = new B //需要进行B => A的隐式转换
}
在这里由于A类和B类没有任何继承关系,应该无法通过编译,但scala compiler会首先尝试搜寻B=>A的隐式转换实例,当找到bToA函数时compiler会把new B替代成bToA(new B),如此这般才能通过编译。
2、当一个类型并不支持某个方法时:
package learn.scalaz
object ab {
class A {
def printA = println("I am A")
}
class B
implicit def bToA(x: B): A = new A
}
object testApp extends App {
import ab._
(new B).printA //需要进行B => A的隐式转换
}
scala compiler 在隐式转换中的隐式解析(implicit resolution)会用以下的策略来查找标示为implicit的实例:
1、能用作用域解析的不带前缀的隐式绑定即:如Bar,而Foo.Bar则不符合要求
这个在以上的例子里已经示范证明了。
2、如果以上方式无法解析隐式转换的话compiler会搜寻目标类型的隐式作用域(implicit scope)内任何对象中的隐式转换。一个类型的隐式作用域(implicit scope)包括了涉及这个类型的所有伴生模块(companion module)内定义的隐式转换。例如:
def foo(implicit p: Foo),这个方法的参数必须是Foo类型。如果compiler无法进行作用域解析的话就必须搜寻隐式作用域内的匹配隐式转换。比如Foo的伴生对象(companion object),如下:
object demo {
object Container {
trait Foo
object Foo {
implicit def x = new Foo {
override def toString = "implicit x"
}
}
}
import Container._
def foo(implicit p: Foo) = println(p) //> foo: (implicit p: scalaz.learn.ex1.Container.Foo)Unit
foo //> implicit x
compiler在object Foo内找到了匹配的隐式转换,程序通过了编译。
由于compiler会首先进行作用域解析,失败后才搜寻隐式转换作用域,所以我们可以把一些默认隐式转换放到隐式作用域里。然后其它编程人员可以通过import来覆载(override)使用他们自己的隐式转换。
综合以上所述:一个类型T的隐式作用域就是组成这个类型的所有类的伴生对象(companion object)。也就是说,T的形成有可能涉及到一组类型。在进行隐式转换解析过程中,compiler会搜寻这些类型的伴生对象。类型T的组成部分如下:
1、所有类型T的父类:
object demo {
object Container {
trait A
trait B
class T extends A with B
object A {
implicit def x = new T {
override def toString = "implicit x"
}
}
}
import Container._
def foo(implicit p: T) = println(p) //> foo: (implicit p: scalaz.learn.demo.Container.Foo)Unit
foo //> implicit x
类型T由A,B组成。compiler从A的伴生对象中解析到隐式转换。
2、如果T是参数化类型,那么所有类型参数的组成类型及包嵌类的组成类型的伴生对象都在隐式转换解析域中。如在解析List[String]中,所有List和String的伴生对象都在解析域中:
object demo {
object Container {
trait A
trait B
class T[A]
object A {
implicit def x = new T[A] {
override def toString = "implicit x"
}
}
}
import Container._
def foo(implicit p: T[A]) = println(p) //> foo: (implicit p: scalaz.learn.demo.Container.T[scalaz.learn.demo.Container.
//| A])Unit
foo //> implicit x
A是T[A]的类型参数。compiler从A的伴生对象中解析到隐式转换。
3、如果T是个单例对象(singleton object),那么T的包嵌对象(container object)就是解析域:
object demo {
object Container {
object T {
def x = "singleton object T"
}
implicit def x = T
}
import Container._
def foo(implicit p: T.type) = println(p.x) //> foo: (implicit p: scalaz.learn.demo.Container.T.type)Unit
foo //> singleton object T
单例对象T定义于包嵌对象Container内。compiler从Container中解析到隐式转换。
这是一篇隐式转换解析原理的讨论,不会对scala有关隐式转换语法和调用做任何解说,希望读者体谅。
Scalaz(1)- 基础篇:隐式转换解析策略-Implicit resolution的更多相关文章
- javascript隐式转换详解
Javascript是web前端开发的必学技术,今天和大家分享的就是javascript的基础知识隐式转换,希望可以帮助大家更好的学习. 转换成布尔类型假 undefined->falSe nu ...
- C#中的类型转换-自定义隐式转换和显式转换
目录 前言 基础知识 示例代码 实际应用 问题 答案 报错 用户定义的转换必须是转换成封闭类型,或者从封闭类型转换 参考 其他 应用和设计 读音 参考 前言 有时我们会遇到这么一种情况:在json数据 ...
- Scala 中的隐式转换和隐式参数
隐式定义是指编译器为了修正类型错误而允许插入到程序中的定义. 举例: 正常情况下"120"/12显然会报错,因为 String 类并没有实现 / 这个方法,我们无法去决定 Stri ...
- Scala 深入浅出实战经典 第61讲:Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析
王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...
- 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则未经解析。
SELECT CONVERT(VARCHAR(100), 列名) FROM Table 提示错误: 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则 ...
- 【C++自我精讲】基础系列五 隐式转换和显示转换
[C++自我精讲]基础系列五 隐式转换和显示转换 0 前言 1)C++的类型转换分为两种,一种为隐式转换,另一种为显式转换. 2)C++中应该尽量不要使用转换,尽量使用显式转换来代替隐式转换. 1 隐 ...
- C#隐式转换和显示转换举例--C#基础
高精度的数据类型转换为低精度的数据类型是显示转换,低精度的转换为高精度的是隐式转换. 温馨提示:不能说强制类型转换是从低精度到高精度. int a=666;float b=(float)a: 由a到b ...
- Java基础学习-类型转换之隐式转换
+是一个运算符,我们应该能够看懂,做数据的加法. boolean类型不能转换为其他的数据类型. 默认转换: byte,short,char--int--float--double by ...
- Spark基础-scala学习(八、隐式转换与隐式参数)
大纲 隐式转换 使用隐式转换加强现有类型 导入隐式转换函数 隐式转换的发生时机 隐式参数 隐式转换 要实现隐式转换,只要程序可见的范围内定义隐式转换函数即可.Scala会自动使用隐式转换函数.隐式转换 ...
随机推荐
- Atititi.名字 姓名 name 起名naming spec 的构成结构规范v2 qc2.docx
Atititi.名字 姓名 name 起名naming spec 的构成结构规范v2 qc2.docx 1.1. 职业名 官职等 amir 阿米尔 放前面1 1.2. 本名1 1.3. 父名,祖名,一 ...
- Atitit.java图片图像处理attilax总结 BufferedImage extends java.awt.Image获取图像像素点image.getRGB(i, lineIndex); 图片剪辑/AtiPlatf_cms/src/com/attilax/img/imgx.javacutImage图片处理titit 判断判断一张图片是否包含另一张小图片 atitit 图片去噪算法的原理与
Atitit.java图片图像处理attilax总结 BufferedImage extends java.awt.Image 获取图像像素点 image.getRGB(i, lineIndex); ...
- fildder 使用方法汇总
作为网络开发人员,怎能不使用一些抓包工具呢?fildder是个不错的选择. 不过,一般情况下,我们往往使用浏览器自带的控制台的[网络]选项就可以达到查看数据的通信情况了,当然,一些浏览器不容易捕捉的事 ...
- fir.im Weekly - 可能是 iOS 审核最全面的解决方案
ipv6 被拒绝,后台定位被拒绝--让很多国内 iOS 开发者心力交瘁.这是一份关于 iOS 审核的终极免费方案,作者iOSWang对最近iOS 审核被拒问题给出了比较全面的方案:Solve-App- ...
- Android笔记——活动的生命周期
一.活动的重要性 掌握活动的生命周期对任何 Android 开发者来说都非常重要,当你深入理解活动的生命周期之后,就可以写出更加连贯流畅的程序,并在如何合理管理应用资源方面,你会发挥的游刃有余.你的应 ...
- Reporting Service 没有权限登陆
在配置好Reporting Service之后,登陆Report Mananger( http://localhost/Reports/Pages/Folder.aspx)出现一个异常,本地用户没有权 ...
- 大型.NET商业软件代码保护技术 技术与实践相结合保护辛苦创造的劳动成果
列举工作以来遇到的各种类型的软件所采用的代码保护技术,只讲原理不涉及技术细节实现,以避免产生法律问题.有些朋友说直接把代码放在Github开源下载,开源可以促进技术交流与进步,然而值钱的代码都积压在硬 ...
- 【转】“正由另一进程使用,因此该进程无法访问该文件”的问题&解决方法
正在写一个手指画图的程序C# + WPF其中有一部分是加载外部某PNG文件,放入BitmapImage,再作为Image的Source显示在Canvas上画了几笔之后,再存回这个PNG文件 ===== ...
- 深入理解PHP内核(九)变量及数据类型-静态变量
原文链接:http://www.orlion.ga/251/ 通常静态变量是静态分配的,他们的生命周期和程序的生命周期一样长,只有在程序退出后才结束生命周期,这和局部变量相反,有的语言中全局变量也是静 ...
- 编译原理:正规式转变成DFA算法
//将正规式转变成NFApackage hjzgg.formal_ceremony_to_dfa; import java.util.ArrayList; class Edge{ public int ...