Scalaz(1)- 基础篇:隐式转换解析策略-Implicit resolution

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

  在正式进入scalaz讨论前我们需要理顺一些基础的scalaz结构组成概念和技巧。scalaz是由即兴多态(ad-hoc polymorphism)类型(typeclass)组成。scalaz typeclass在scala中的应用有赖于scala compiler的一项特别功能:隐式转换(implicit conversion),使程序表述更精简。由于隐式转换是一项compiler功能,在程序编译(compile)的时候是由compiler来进行类型转换代码的产生和替代的。

  让我们先了解一下作用域(scope)和绑定(binding)。这两样都是在编译程序时compiler需要解决的问题。所谓作用域解析(scope resolution)就是要确定一个绑定在一个作用域里是可视的,否则程序无法通过编译。

作用域就是一个绑定在一个程序范围内的可视型。作用域可以是某个类的内部或者是某个方法或函数的内部,基本上用{}就可以创造一个新的作用域了。在scala作用域可以是多层的,一个域可以存在于另一个作用域内。外部域的绑定在内部域内是可视的,反之则不然:


1 class Foo(x: Int) {
2   def temp = {
3     val y = x + 1  //x是本地域外的一个绑定
4   }  
5 }

在以上的例子里x在temp{}内是可视的。一个作用域内的绑定可以屏蔽(shadow)外域定义的绑定:


1 class Foo(x: Int) {
2   def temp = {
3     val x = 0      //本地域绑定。屏蔽了外域的x
4     val y = x + 1  //y=1,x是本地域的一个绑定
5   }  
6 }

绑定屏蔽是分优先次序如下:

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有关隐式转换语法和调用做任何解说,希望读者体谅。



相关文章
|
26天前
|
SQL 安全 算法
网络安全与信息安全的全面解析:应对漏洞、加密技术及提升安全意识的策略
本文深入探讨了网络安全和信息安全的重要性,详细分析了常见的网络安全漏洞以及其利用方式,介绍了当前流行的加密技术及其应用,并强调了培养良好安全意识的必要性。通过综合运用这些策略,可以有效提升个人和企业的网络安全防护水平。
|
3天前
|
数据采集 安全 数据管理
深度解析:DataHub的数据集成与管理策略
【10月更文挑战第23天】DataHub 是阿里云推出的一款数据集成与管理平台,旨在帮助企业高效地处理和管理多源异构数据。作为一名已经有一定 DataHub 使用经验的技术人员,我深知其在数据集成与管理方面的强大功能。本文将从个人的角度出发,深入探讨 DataHub 的核心技术、工作原理,以及如何实现多源异构数据的高效集成、数据清洗与转换、数据权限管理和安全控制措施。通过具体的案例分析,展示 DataHub 在解决复杂数据管理问题上的优势。
15 1
|
27天前
|
安全 编译器 程序员
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
【C++篇】C++类与对象深度解析(六):全面剖析拷贝省略、RVO、NRVO优化策略
42 2
|
27天前
|
运维 负载均衡 安全
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
深度解析:Python Web前后端分离架构中WebSocket的选型与实现策略
78 0
|
3天前
|
数据采集 机器学习/深度学习 数据挖掘
10种数据预处理中的数据泄露模式解析:识别与避免策略
在机器学习中,数据泄露是一个常见问题,指的是测试数据在数据准备阶段无意中混入训练数据,导致模型在测试集上的表现失真。本文详细探讨了数据预处理步骤中的数据泄露问题,包括缺失值填充、分类编码、数据缩放、离散化和重采样,并提供了具体的代码示例,展示了如何避免数据泄露,确保模型的测试结果可靠。
14 2
|
16天前
|
机器学习/深度学习 人工智能 算法
揭开深度学习与传统机器学习的神秘面纱:从理论差异到实战代码详解两者间的选择与应用策略全面解析
【10月更文挑战第10天】本文探讨了深度学习与传统机器学习的区别,通过图像识别和语音处理等领域的应用案例,展示了深度学习在自动特征学习和处理大规模数据方面的优势。文中还提供了一个Python代码示例,使用TensorFlow构建多层感知器(MLP)并与Scikit-learn中的逻辑回归模型进行对比,进一步说明了两者的不同特点。
46 2
|
18天前
|
存储 缓存 监控
深入解析:Elasticsearch集群性能调优策略与最佳实践
【10月更文挑战第8天】Elasticsearch 是一个分布式的、基于 RESTful 风格的搜索和数据分析引擎,它能够快速地存储、搜索和分析大量数据。随着企业对实时数据处理需求的增长,Elasticsearch 被广泛应用于日志分析、全文搜索、安全信息和事件管理(SIEM)等领域。然而,为了确保 Elasticsearch 集群能够高效运行并满足业务需求,需要进行一系列的性能调优工作。
39 3
|
23天前
|
SQL Oracle 关系型数据库
SQL整库导出语录:全面解析与高效执行策略
在数据库管理和维护过程中,整库导出是一项常见的需求,无论是为了备份、迁移还是数据分析,掌握如何高效、准确地导出整个数据库至关重要
|
25天前
|
数据采集 监控 架构师
主数据管理实施方案:规划与具体策略的全面解析
在当今数字化转型的浪潮中,主数据管理(MDM, Master Data Management)已成为企业提升数据质量、优化业务流程、增强决策能力的重要基石。一个成功的主数据管理实施方案不仅需要周密的规划,还需要具体可行的策略来确保项目顺利推进并达到预期效果。
|
14天前
|
缓存 前端开发 安全
前端开发者必备:HTTP状态码含义与用途解析,常见错误码产生原因及解决策略
前端开发者必备:HTTP状态码含义与用途解析,常见错误码产生原因及解决策略
68 0

推荐镜像

更多