scalaz功能基本上由以下三部分组成:

1、新的数据类型,如:Validation, NonEmptyList ...

2、标准scala类型的延伸类型,如:OptionOps, ListOps ...

3、通过typeclass的随意多态(ad-hoc polymorphism)编程模式实现的大量概括性函数组件库

我们在这篇重点讨论多态(polymorphism),特别是随意多态(ad-hoc polymorphism)。

多态,简单来讲就是一项操作(operation)可以对任意类型施用。在OOP的世界里,我们可以通过以下各种方式来实现多态:

1、重载 Overloading

2、继承 Inheritance

3、模式匹配 Pattern-matching

4、特性 Traits/interfaces

5、类型参数 Type parameters

作为一种通用的组件库,scalaz是通过任意多态typeclass模式来实现软件模块之间的松散耦合(decoupling).这样scalaz的用户就可以在不需要重新编译scalaz源代码的情况下对任何类型施用scalaz提供的函数功能了。

我们来分析一下各种实现多态的方式:

假如我们设计一个描述输入参数的函数:tell(t: some type): String

如:tell(c: Color) >>> "I am color Red"

tell(i: Int) >>> "I am Int 3"

tell(p: Person) >>> "I am Peter"

如果用Overloading:

 object overload {
case class Color(descript: String)
case class Person(name: String) def tell(c: Color) = "I am color "+ c.descript
def tell(p: Person) = "I am "+ p.name
}

我们看到用重载的话,除了相同的函数名称tell之外这两个函数没有任何其它关系。我们必须对每一个不同的类型提供一个独立的tell函数。这种方式没什么用,我们需要的是一个函数施用在不同的类型上。

再试试用继承Inheritance:

 trait Thing {
def tell: String
}
class Color(descript: String) extends Thing {
override def tell: String = "I am color " + descript
}
class Person(name: String) extends Thing {
override def tell: String = "I am " + name
} new Color("RED").tell //> res0: String = I am color RED
new Person("John").tell //> res1: String = I am John

这种方式更糟糕,tell和类有着更强的耦合。用户必须拥有这些类的源代码才能实现tell。试想如果这个类型是标准的Int怎么办。

用模式匹配pattern-matching呢?

 case class Color(descript: String)
case class Person(name: String)
def tell(x: Any): String = x match {
case Color(descr) => "I am color " + descr
case Person(name) => "I am " + name
case i: Int => "I am Int "+i
case _ => "unknown"
} //> tell: (x: Any)String tell() //> res0: String = I am Int 23
tell(Color("RED")) //> res1: String = I am color RED
tell(Person("Peter")) //> res2: String = I am Peter

Pattern-matching倒是可以把tell和类型分开。但是必须在tell里增加新的类型匹配,也就是说必须能控制tell的源代码。

现在再尝试用typeclass模式:typeclass模式是由trait加implicit组成。先看看trait:

 trait Tellable[T] {
def tell(t: T): String
}

这个trait Tellable代表的意思是把tell功能附加到任意类型T,但还未定义tell的具体功能。

如果用户想把tell附加给Color类型:

 trait Tellable[T] {
def tell(t: T): String
}
case class Color(descript: String)
case class Person(name: String)
object colorTeller extends Tellable[Color] {
def tell(t: Color): String = "I am color "+t.descript
}

针对Color我们在object colorTeller里实现了tell。现在更概括的tell变成这样:

 def tell[T](t: T)(M: Tellable[T]) = {
M.tell(t)
} //> tell: [T](t: T)(M: scalaz.learn.demo.Tellable[T])String
tell(Color("RED"))(colorTeller) //> res0: String = I am color RED

这个版本的tell增加了类型变量T、输入参数M,意思是对任何类型T,因为M可以对任何类型T施用tell,所以这个版本的tell可以在任何类型上施用。上面的例子调用了针对Color类型的tell。那么针对Person的tell我们再实现一个Tellable[Person]实例就行了吧:

 val personTeller = new Tellable[Person] {
def tell(t: Person): String = "I am "+ t.name
} //> personTeller : scalaz.learn.demo.Tellable[scalaz.learn.demo.Person] = scala
//| z.learn.demo$$anonfun$main$1$$anon$1@13969fbe
tell(Person("John"))(personTeller) //> res1: String = I am John
val intTeller = new Tellable[Int] {
def tell(t: Int): String = "I am Int "+ t.toString
} //> intTeller : scalaz.learn.demo.Tellable[Int] = scalaz.learn.demo$$anonfun$ma
//| in$1$$anon$2@6aaa5eb0
tell()(intTeller) //> res2: String = I am Int 43

如上,即使针对Int类型我们一样可以调用这个tell[T]。也既是说如果这个概括性的tell[T]是由其他人开发的某些组件库提供的,那么用户只要针对他所需要处理的类型提供一个tell实现实例,然后调用这个共享的tell[T],就可以得到随意多态效果了。至于这个类型的实现细节或者源代码则不在考虑之列。

好了,现在我们可以用implicit来精简tell[T]的表达形式:

 def tell[T](t: T)(implicit M: Tellable[T]) = {
M.tell(t)
} //> tell: [T](t: T)(implicit M: scalaz.learn.demo.Tellable[T])String

也可以这样写:

 def tell[T : Tellable](t: T) = {
implicitly[Tellable[T]].tell(t)
} //> tell: [T](t: T)(implicit evidence$1: scalaz.learn.demo.Tellable[T])String

现在看看如何调用tell:

 implicit object colorTeller extends Tellable[Color] {
def tell(t: Color): String = "I am color "+t.descript
} tell(Color("RED")) //> res0: String = I am color RED implicit val personTeller = new Tellable[Person] {
def tell(t: Person): String = "I am "+ t.name
} //> personTeller : scalaz.learn.demo.Tellable[scalaz.learn.demo.Person] = scala
//| z.learn.demo$$anonfun$main$1$$anon$1@3498ed
tell(Person("John")) //> res1: String = I am John implicit val intTeller = new Tellable[Int] {
def tell(t: Int): String = "I am Int "+ t.toString
} //> intTeller : scalaz.learn.demo.Tellable[Int] = scalaz.learn.demo$$anonfun$ma
//| in$1$$anon$2@1a407d53
tell() //> res2: String = I am Int 43

假如我忽然需要针对新的类型List[Color], 我肯定无须理会tell[T],只要调用它就行了:

 implicit object listTeller extends Tellable[List[Color]] {
def tell(t: List[Color]): String = {
(t.map(c => c.descript)).mkString("I am list of color [",",","]")
}
} tell[List[Color]](List(Color("RED"),Color("BLACK"),Color("YELLOW"),Color("BLUE")))
//> res3: String = I am list of color [RED,BLACK,YELLOW,BLUE]

这才是真正的随意多态。

值得注意的是implicit是scala compiler的一项功能。在编译时compiler发现类型不对称就会进行隐式转换解析(implicit resolution)。如果解析失败则程序无法通过编译。如果我这样写: tell(4.5),compiler会提示语法错误。而上面其它多态方式则必须在运算时(runtime)才能发现错误。

Scalaz(2)- 基础篇:随意多态-typeclass, ad-hoc polymorphism的更多相关文章

  1. Ad hoc polymorphism

    与面向对象中的接口类或抽象类中定义的函数组类似: 函数的具体执行依赖与函数医用的类型. In programming languages, ad-hoc polymorphism[1] is a ki ...

  2. Type class-Typeclass-泛型基础上的二次抽象---随意多态

    对泛型的类型添加约束,从而使泛型类型的变量具有某种通用操作. 再使用这些操作,参与到其它操作中. In computer science, a type class is a type system  ...

  3. Scalaz(3)- 基础篇:函数概括化-Generalizing Functions

    Scalaz是个通用的函数式编程组件库.它提供的类型.函数组件都必须具有高度的概括性才能同时支持不同数据类型的操作.可以说,scalaz提供了一整套所有编程人员都需要的具有高度概括性的通用函数,它是通 ...

  4. java基础篇 之 构造器内部的多态行为

    java基础篇 之 构造器内部的多态行为 ​ 我们来看下下面这段代码: public class Main { public static void main(String[] args) { new ...

  5. python面试题库——1Python基础篇

    第一部分 Python基础篇(80题) 为什么学习Python? 语言本身简洁,优美,功能超级强大,跨平台,从桌面应用,web开发,自动化测试运维,爬虫,人工智能,大数据处理都能做 Python和Ja ...

  6. 夯实Java基础系列1:Java面向对象三大特性(基础篇)

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 [https://github.com/h2pl/Java-Tutorial](https: ...

  7. Python3学习(1)-基础篇

    Python3学习(1)-基础篇 Python3学习(2)-中级篇 Python3学习(3)-高级篇 安装(MAC) 直接运行: brew install python3 输入:python3 --v ...

  8. VBS基础篇 - 对象(1) - Class对象

    VBS基础篇 - 对象(1) - Class对象   相信对JAVA有一定了解的朋友一定对类这个名词不陌生,但是大家可能没有想过在VBS中使用Class类吧,其实Class类在自动化测试中是相当常用的 ...

  9. Qt入门之基础篇(三):掌握Qt4的静态编译基本方法

    转载载请注明出处:CN_Simo. 导语: 前两章都提到过“静态编译”(Static Compilation),在Windows下一次静态编译差不多需要长达三个小时才能完成,而且还非常容易由于各种原因 ...

随机推荐

  1. Atitit 知识图谱解决方案:提供完整知识体系架构的搜索与知识结果overview

    Atitit 知识图谱解决方案:提供完整知识体系架构的搜索与知识结果overview   知识图谱的表示和在搜索中的展1 提升Google搜索效果3 1.找到最想要的信息.3 2.提供最全面的摘要.4 ...

  2. ASP.NET MVC的Action拦截器(过滤器)ActionFilter

    有时项目要进行客户端请求(action)进行拦截(过滤)验证等业务,可以使用拦截器进行实现,所谓的action拦截器也没有什么的,只是写一个类,继承另一个类(System.Web.Mvc.Filter ...

  3. Jquery判断数组中是否包含某个元素$.inArray()的用法

    判断数组里面是否包含某个元素可以使用 $.inArray("元素(字符串)",数组名称) 进行判断 ,当存在该元素(字符串)时,返回该元素在数组的下标,不存在时返回 -1 示例代码 ...

  4. app的同源和域的问题

    app的同源和域的问题

  5. 25.按要求编写一个Java应用程序: (1)编写一个矩形类Rect,包含: 两个属性:矩形的宽width;矩形的高height。 两个构造方法: 1.一个带有两个参数的构造方法,用于将width和height属性初化; 2.一个不带参数的构造方法,将矩形初始化为宽和高都为10。 两个方法: 求矩形面积的方法area() 求矩形周长的方法perimeter() (2)通过继承Rect类编写一个具有

    package zhongqiuzuoye; //自己写的方法 public class Rect { public double width; public double height; Rect( ...

  6. CSS3妙用

    scaleX的妙用 案例1 效果: HTML: <a href="javascript:;">我有下划线噢</a> CSS: a{ text-decorat ...

  7. .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引

    系列文章索引: .NET面试题解析(01)-值类型与引用类型 .NET面试题解析(02)-拆箱与装箱 .NET面试题解析(03)-string与字符操作 .NET面试题解析(04)-类型.方法与继承 ...

  8. 深入理解CSS计数器

    × 目录 [1]创建计数器 [2]使用计数器 [3]DEMO 前面的话 我们对计数器已经不陌生了,有序列表中的列表项标志就是计数器. 创建计数器 创建计数器的基础包括两个方面,一是能重置计数器的起点, ...

  9. 吐槽坑爹的微软win store app审核

    从学习win store app 开发到做出第一个应用 博客园cnblogs 花了一个多月的全部业余和上班空闲时间, 上周在端午节放假期间终于完成了计划的全部开发和测试, 6月10号怀着无比激动的心情 ...

  10. Windows Azure Web Site (11) 使用源代码管理器管理Azure Web Site

    <Windows Azure Platform 系列文章目录> 熟悉Azure Web Site平台的读者都知道,我们可以通过FTP等方式,把本地的Web Application部署到微软 ...