在正式进入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的更多相关文章

  1. javascript隐式转换详解

    Javascript是web前端开发的必学技术,今天和大家分享的就是javascript的基础知识隐式转换,希望可以帮助大家更好的学习. 转换成布尔类型假 undefined->falSe nu ...

  2. C#中的类型转换-自定义隐式转换和显式转换

    目录 前言 基础知识 示例代码 实际应用 问题 答案 报错 用户定义的转换必须是转换成封闭类型,或者从封闭类型转换 参考 其他 应用和设计 读音 参考 前言 有时我们会遇到这么一种情况:在json数据 ...

  3. Scala 中的隐式转换和隐式参数

    隐式定义是指编译器为了修正类型错误而允许插入到程序中的定义. 举例: 正常情况下"120"/12显然会报错,因为 String 类并没有实现 / 这个方法,我们无法去决定 Stri ...

  4. Scala 深入浅出实战经典 第61讲:Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  5. 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则未经解析。

    SELECT CONVERT(VARCHAR(100), 列名) FROM Table 提示错误: 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则 ...

  6. 【C++自我精讲】基础系列五 隐式转换和显示转换

    [C++自我精讲]基础系列五 隐式转换和显示转换 0 前言 1)C++的类型转换分为两种,一种为隐式转换,另一种为显式转换. 2)C++中应该尽量不要使用转换,尽量使用显式转换来代替隐式转换. 1 隐 ...

  7. C#隐式转换和显示转换举例--C#基础

    高精度的数据类型转换为低精度的数据类型是显示转换,低精度的转换为高精度的是隐式转换. 温馨提示:不能说强制类型转换是从低精度到高精度. int a=666;float b=(float)a: 由a到b ...

  8. Java基础学习-类型转换之隐式转换

    +是一个运算符,我们应该能够看懂,做数据的加法. boolean类型不能转换为其他的数据类型. 默认转换:     byte,short,char--int--float--double     by ...

  9. Spark基础-scala学习(八、隐式转换与隐式参数)

    大纲 隐式转换 使用隐式转换加强现有类型 导入隐式转换函数 隐式转换的发生时机 隐式参数 隐式转换 要实现隐式转换,只要程序可见的范围内定义隐式转换函数即可.Scala会自动使用隐式转换函数.隐式转换 ...

随机推荐

  1. Android WebView 总结 —— 使用HTML5播放视频及全屏方案

    在APP开发的过程中,会碰到需要在WebView中播放视频的需求,下面讲解一下如何在WebView中使用html5播放视频. 1.让视频在各个Android版本能够正常播放 在AndroidManif ...

  2. win环境安装python爬虫框架scrapy

    #官网下载python for windows #https://www.python.org/downloads/ #安装后在“计算机->属性->高级系统设置->环境变量-> ...

  3. 中小型ERP系统开发与实施

    1. 能反映企业管理的各个方面和解决企业实际的问题,不限于一般问题的解决,而是深入企业的业务过程 2. 在此软件的背后有真正的管理思想(不是泛泛而言)和对管理的精髓理解 和归纳,有一个整体的较详细的规 ...

  4. ObjectOutputStream和ObjectInputStream

    官方解释: ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream.可以使用 ObjectInputStream 读取(重构)对象.通过使用流中的文 ...

  5. 编译原理(简单自动词法分析器LEX)

    编译原理(简单自动词法分析器LEX)源程序下载地址:  http://files.cnblogs.com/files/hujunzheng/%E6%B1%87%E7%BC%96%E5%8E%9F%E7 ...

  6. BonBon - 使用 CSS3 制作甜美的糖果按钮

    BonBon 是一组使用 CSS3 制作的甜美的糖果按钮样式.在过去,我们都是使用图片或者 JavaScript 来实现漂亮的按钮效果,随着越来越多的浏览器对 CSS3 的支持和完善,使用 CSS3 ...

  7. 使用 CSS3 制作一组超时尚的动画按钮效果

    通过 CSS3 的新特性,我们创作出好的交互和效果的可能性大大增加.这篇文章中,我想与大家分享一些 CSS3 动画按钮效果.我们的想法是创建一个具有不同风格的一些动画链接元素,鼠标悬停时有动画效果和活 ...

  8. Cordova 讲义 1 – 周金根

    讲义下载地址见:   http://www.zhoujingen.cn/blog/7905.html   最后下载链接   移动应用形成了iOS.Android和windows phone三大阵营: ...

  9. 基于HTML5的Web SCADA工控移动应用

    在电力.油田燃气.供水管网等工业自动化领域Web SCADA的概念已经提出了多年,早先年的Web SCADA前端技术大部分还是基于Flex.Silverlight甚至Applet这样的重客户端方案,在 ...

  10. 用redux构建购物车

    很久没更新博客了,最近要用到react,再来跟大家分享一个redux案例吧. [ {"id": 1, "title": "iPad 4 Mini&qu ...