在正式进入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. Atitit 图像处理 常用8大滤镜效果 Jhlabs 图像处理类库 java常用图像处理类库

    Atitit 图像处理 常用8大滤镜效果 Jhlabs 图像处理类库 java常用图像处理类库1.1. 5种常用的Photoshop滤镜,分别针对照片的曝光.风格色调.黑白照片处理.锐利度.降噪这五大 ...

  2. paip. 解决php 以及 python 连接access无效的参数量。参数不足,期待是 1”的错误

    paip. 解决php 以及 python 连接access无效的参数量.参数不足,期待是 1"的错误 作者Attilax  艾龙,  EMAIL:1466519819@qq.com  来源 ...

  3. JS之模板技术(aui / artTemplate)

    artTemplate是个好东西啊,一个开源的js前端模板引擎,使用简单,渲染效率特别的高. 我经常使用这个技术来在前端动态生成新闻列表,排行榜,历史记录等需要在前端列表显示的信息. 下面是artTe ...

  4. 文本溢出text-overflow和文本阴影text-shadow

    前面的话 CSS3新增了一些关于文本的样式,其中text-overflow文本溢出和text-shadow文本阴影有些特别.因为它们有对应的overflow溢出属性和box-shadow盒子阴影属性. ...

  5. Network - SNMP

    Simple Network Management ProtocolWikipediahttps://en.wikipedia.org/wiki/Simple_Network_Management_P ...

  6. 新人学习Android开发遇到的小问题总结

    1. IDE搭建: 搭建android的IDE时,先注意是什么版本的系统,64/32位系统. 通常使用的是Eclipse for android,Android Studio由于还需要FQ,网速慢,所 ...

  7. 也来玩玩 javascript对象深拷贝,浅拷贝

    经常看到讨论c#深拷贝,浅拷贝的博客,最近js写的比较多, 所以也来玩玩js的对象拷贝. 下面是维基百科对深浅拷贝的解释: 浅拷贝 One method of copying an object is ...

  8. 你必须知道ASP.NET知识------从IIS到httpmodule(第一篇)

    一.写在前面 最近有时间,顺便将这系列洗完,接着上文:IIS各个版本知识总结 这篇文章原本计划写到HttpHandler为止,但限于篇幅就写到httpmodule 本文有不足之处,求指正,希望我能将它 ...

  9. SQL2005四个排名函数(row_number、rank、dense_rank和ntile)的比较

    排名函数是SQL Server2005新加的功能.在SQL Server2005中有如下四个排名函数: .row_number .rank .dense_rank .ntile 下面分别介绍一下这四个 ...

  10. a configuration error occurred during startup. place verify the preference field whth the prompt:TomcatJDK name:

    错误一:安装Tomcat的时候出现了错误,这里要是找不到Tomcat6.0我们可以设置其enbale显示就可以,上面的错误解决办法: 错误二:在eclipse中启动Tomcat6.0的时候现在说800 ...