Scalaz是由一堆的typeclass组成。每一个typeclass具备自己特殊的功能。用户可以通过随意多态(ad-hoc polymorphism)把这些功能施用在自己定义的类型上。scala这个编程语言借鉴了纯函数编程语言Haskell的许多概念。typeclass这个名字就是从Haskell里引用过来的。只不过在Haskell里用的名称是type class两个分开的字。因为scala是个OOP和FP多范畴语言,为了避免与OOP里的type和class发生混扰,所以就用了typeclass一个字。实际上scalaz就是Haskell基本库里大量typeclass的scala实现。

在这篇讨论里我们可以通过介绍scalaz的一些比较简单的typeclass来了解scalaz typeclass的实现、应用方法以及scalaz函数库的内部结构。

我们首先看看Equal:这是个比较典型的typeclass,适合用来介绍scalaz typeclass的一些实现方式、应用模式以及函数库结构。

我们知道,scalaz typeclass的几个重要元素就是:

1、特质 trait

2、隐式实例 implicit instances

3、方法注入 method injection

Equal Trait 在 core/.../scalaz/Equal.scala里,比较简单:

 trait Equal[F]  { self =>
////
def equal(a1: F, a2: F): Boolean def contramap[G](f: G => F): Equal[G] = new Equal[G] {
def equal(a1: G, a2: G) = self.equal(f(a1), f(a2))
} /** @return true, if `equal(f1, f2)` is known to be equivalent to `f1 == f2` */
def equalIsNatural: Boolean = false
...

只要实现equal(a1,a2)这个抽象函数就可以了。Equal typeclass主要的功能就是对两个相同类型的元素进行等比。那和标准的 == 符号什么区别呢?Equal typeclass提供的是类型安全(type safe)的等比,在编译时由compiler发现错误,如下面的例子:

 cala>  == 2.0
res3: Boolean = true scala> === 2.0
<console>:: error: type mismatch;
found : Double(2.0)
required: Int
=== 2.0
^

以上的 === 是Equal typeclass的符号方法(symbolic method),就是这个equal(a1,a2),是通过方法注入加入到Equal typeclass里的。我们可以看到equal对两个比对对象的类型要求是非常严格的,否则无法通过编译(除非在隐式作用域implicit scode内定义Double到Int的隐式转换implicit conversion)。

但是,在Equal Trait里的equal是个抽象函数(abstract function),没有实现。那么肯定在隐式作用域(implicit scope)里存在着隐式Equal实例。比如以上的例子我们应该试着找找Equal的Int实例。

我在scalaz.std/AnyValue.scala里发现了这段代码:

   implicit val intInstance: Monoid[Int] with Enum[Int] with Show[Int] = new Monoid[Int] with Enum[Int] with Show[Int] {
override def shows(f: Int) = f.toString def append(f1: Int, f2: => Int) = f1 + f2 def zero: Int = def order(x: Int, y: Int) = if (x < y) Ordering.LT else if (x == y) Ordering.EQ else Ordering.GT def succ(b: Int) = b +
def pred(b: Int) = b -
override def succn(a: Int, b: Int) = b + a
override def predn(a: Int, b: Int) = b - a
override def min = Some(Int.MinValue)
override def max = Some(Int.MaxValue) override def equalIsNatural: Boolean = true
}

这是个Int实例。但好像没有继承Equal trait,因而也没有发现equal函数的实现。但是它继承了Enum。那么在scalaz/Enum.scala中的Enum trait是这样的:

 trait Enum[F] extends Order[F] { self =>

Enum又继承了Order,再到scalaz/order.scala看看Order trait:

 trait Order[F] extends Equal[F] { self =>
////
def apply(x: F, y: F): Ordering = order(x, y) def order(x: F, y: F): Ordering def equal(x: F, y: F): Boolean = order(x, y) == Ordering.EQ

原来Order就是Equal,所以Enum就是Equal。equal(a1,a2)是在Order trait里用order(a1,a2)实现的,而order(a1,a2)是在Int隐式实例intInstance里实现了。这样就解决了隐式实例的问题,所以我们可以使用 2.===(2.0) >>> 2 === 2.0这样的语法。

我们再来看看方法注入是怎么实现的吧。Scalaz方法注入标准写法:放在scalaz/syntax/EqualSyntax.scala里:

 /** Wraps a value `self` and provides methods related to `Equal` */
final class EqualOps[F] private[syntax](val self: F)(implicit val F: Equal[F]) extends Ops[F] {
//// final def ===(other: F): Boolean = F.equal(self, other)
final def /==(other: F): Boolean = !F.equal(self, other)
final def =/=(other: F): Boolean = /==(other)
final def ≟(other: F): Boolean = F.equal(self, other)
final def ≠(other: F): Boolean = !F.equal(self, other) /** Raises an exception unless self === other. */
final def assert_===[B](other: B)(implicit S: Show[F], ev: B <:< F) =
if (/==(other)) sys.error(S.shows(self) + " ≠ " + S.shows(ev(other))) ////
}

scalaz一般把字符方法(symbolic method)放在scalaz/syntax目录下。也就是 ===, =/=这两个操作符号,对应的是 ==, !=这两个标准操作符。注意这个符号方法容器类EqualOps需要一个隐式参数(implicit parameter)F: Equal[F],因为具体的equal(a1,a2)是在Equal[F]的实例里实现的。具体的方法注入黏贴还是通过隐式解析实现的:

 trait ToEqualOps  {
implicit def ToEqualOps[F](v: F)(implicit F0: Equal[F]) =
new EqualOps[F](v) //// ////
}

但是这个隐式转换ToEqualOps为什么是在trait里?隐式作用域必须是在某个object里的。我们再看看scalaz/syntax/syntax.scala里的这一段代码;

 trait ToTypeClassOps
extends ToSemigroupOps with ToMonoidOps with ToEqualOps with ToShowOps
with ToOrderOps with ToEnumOps with ToPlusEmptyOps
with ToFunctorOps with ToContravariantOps with ToApplyOps
with ToApplicativeOps with ToBindOps with ToMonadOps with ToComonadOps
with ToBifoldableOps with ToCozipOps
with ToPlusOps with ToApplicativePlusOps with ToMonadPlusOps with ToTraverseOps with ToBifunctorOps
with ToBitraverseOps with ToComposeOps with ToCategoryOps
with ToArrowOps with ToFoldableOps with ToChoiceOps with ToSplitOps with ToZipOps with ToUnzipOps with ToMonadTellOps with ToMonadListenOps with ToMonadErrorOps
with ToFoldable1Ops with ToTraverse1Ops with ToOptionalOps with ToCatchableOps with ToAlignOps

trait ToTypeClassOps继承了ToEqualOps。然后在scalaz/Scalaz.scala里:

 object Scalaz
extends StateFunctions // Functions related to the state monad
with syntax.ToTypeClassOps // syntax associated with type classes
with syntax.ToDataOps // syntax associated with Scalaz data structures
with std.AllInstances // Type class instances for the standard library types
with std.AllFunctions // Functions related to standard library types
with syntax.std.ToAllStdOps // syntax associated with standard library types
with IdInstances // Identity type and instances

object Scalaz继承了ToTypeClassOps。这样ToEqualOps的隐式作用域就在object Scalaz里了。

为了方便使用,Equal typeclass提供了构建函数:

   def equal[A](f: (A, A) => Boolean): Equal[A] = new Equal[A] {
def equal(a1: A, a2: A) = f(a1, a2)
}

我们可以这样构建Equal实例:

 scala> case class Person(name: String, age: Int)
defined class Person
scala> implicit val personEqual: Equal[Person] = Equal.equal{(a,b) => a.name == b.name && a.age == b.age}
personEqual: scalaz.Equal[Person] = scalaz.Equal$$anon$@7e5716e scala> Person("Jone",) === Person("Jone",)
res0: Boolean = true scala> Person("Jone",) === Person("Jone",)
res1: Boolean = false scala> Person("Jone",) === Person("John",)
res2: Boolean = false

当然我们也可以通过实现抽象函数equal(a1,a2)函数的方式来构建Equal实例:

 scala> implicit val personEqual = new Equal[Person] {
| def equal(a1: Person, a2: Person): Boolean = a1.name == a2.name && a1.age == a2.age
| }
personEqual: scalaz.Equal[Person] = $anon$@247cc8f scala> Person("John",) === Person("Joe",)
res0: Boolean = false scala> Person("John",) === Person("John",)
res1: Boolean = true

在Equal trait 里有个有趣的函数:

  def contramap[G](f: G => F): Equal[G] = new Equal[G] {
def equal(a1: G, a2: G) = self.equal(f(a1), f(a2))
}

从函数名称来看它是个逆变(contra)。把函数款式概括化如下:

def contramap[G](f: G => F): Equal[F] => Equal[G]

它的意思是说:如果提供G => F转换关系,就可以把Equal[F]转成Equal[G]。与正常的转换函数map比较:

def map[G](f: F => G): Equal[F] => Equal[G]

函数f是反方向的,因而称之逆变contramap。Equal的伴生对象提供了另外一个构建函数:

   def equalBy[A, B: Equal](f: A => B): Equal[A] = Equal[B] contramap f

equalBy的意思是:假如已经有了Equal[B]实例,如果能提供A => B得转换,就可以通过equalBy构建Equal[A]实例。

举例:case class MoneyCents(cents: Int)

我们有现成的Equal[Int]实例,只要能提供MoneyCents与Int之间的转换关系,我们就可以等比MoneyCents了:

 scala> case class MoneyCents(cents: Int)
defined class MoneyCents
scala> def moneyToInt(m: MoneyCents): Int = m.cents *
moneyToInt: (m: MoneyCents)Int scala> implicit val moneyEqual: Equal[MoneyCents] = Equal.equalBy(moneyToInt)
moneyEqual: scalaz.Equal[MoneyCents] = scalaz.Order$$anon$@138ad7f5 scala> MoneyCents() === MoneyCents()
res2: Boolean = true scala> MoneyCents() === MoneyCents()
res3: Boolean = false

这个逆变在以上例子的主要用途是:我们知道如何等比Int,我们又可以提供MoneyCents和Int之间的转换关系,那么我们就可以构建Equal[MoneyCents]实例。

介绍了Equal typeclass的实现和应用原理后,解释其它的typeclass就简单许多了。

我们再看看Order typeclass:

Scalaz的Order tyeclass提供了一组操作符号:在scalaz/syntax/OrderSyntax.scala里

 /** Wraps a value `self` and provides methods related to `Order` */
final class OrderOps[F] private[syntax](val self: F)(implicit val F: Order[F]) extends Ops[F] {
////
final def <(other: F): Boolean = F.lessThan(self, other)
final def <=(other: F): Boolean = F.lessThanOrEqual(self, other)
final def >(other: F): Boolean = F.greaterThan(self, other)
final def >=(other: F): Boolean = F.greaterThanOrEqual(self, other)
final def max(other: F): F = F.max(self, other)
final def min(other: F): F = F.min(self, other)
final def cmp(other: F): Ordering = F.order(self, other)
final def ?|?(other: F): Ordering = F.order(self, other)
final def lte(other: F): Boolean = F.lessThanOrEqual(self, other)
final def gte(other: F): Boolean = F.greaterThanOrEqual(self, other)
final def lt(other: F): Boolean = F.lessThan(self, other)
final def gt(other: F): Boolean = F.greaterThan(self, other)
////
}

其中cmp(?|?)方法使用了Ordering类型。Ordering是另外一个typeclass: scalaz/Ordering.scala

 object Ordering extends OrderingInstances with OrderingFunctions {
case object LT extends Ordering(-, "LT") { def complement = GT }
case object EQ extends Ordering(, "EQ") { def complement = EQ }
case object GT extends Ordering(, "GT") { def complement = LT }
}

主要定义了LT,EQ,GT三个状态。

我们应该尽量使用lt,lte,gt,gte来确保类型安全(让compiler来发现错误):

 scala>  < 1.0
res4: Boolean = false scala> lt 1.0
<console>:: error: type mismatch;
found : Double(1.0)
required: Int
lt 1.0
^ scala> ?|? 1.0
<console>:: error: type mismatch;
found : Double(1.0)
required: Int
?|? 1.0
^ scala> ?|?
res7: scalaz.Ordering = LT scala> lt
res8: Boolean = true

与Equal typeclass 同样,如果我们需要在自定义的类型T上使用Order typeclass的话,有几种方法可以构建Order[T]:

1、实现Order trait抽象函数order(a1,a2),在scalaz/std/AnyValue.scala中的Int实例intInstance中是这样实现order(a1,a2)函数的:

     def order(x: Int, y: Int) = if (x < y) Ordering.LT else if (x == y) Ordering.EQ else Ordering.GT

我们可以在Person类型上使用Order:

 scala> case class Person(name: String, age: Int)
defined class Person scala> implicit val personAgeOrder = new Order[Person] {
| def order(a1: Person, a2: Person): Ordering =
| if (a1.age < a2.age) Ordering.LT else if (a1.age > a2.age) Ordering.GT else Ordering.EQ
| }
personAgeOrder: scalaz.Order[Person] = $anon$@736d65e9
scala> Person("John",) ?|? Person("Joe",)
res11: scalaz.Ordering = LT scala> Person("John",) lt Person("Joe",)
res12: Boolean = true scala> Person("John",) gt Person("Joe",)
res13: Boolean = false

2、用object Order里的构建函数order[A](f: (A,A) => Ordering): Order[A]

 scala> case class Meat(cat: String, weight: Int)
defined class Meat
scala> implicit val meatWeightOrder: Order[Meat] = Order.order(_.weight ?|? _.weight)
meatWeightOrder: scalaz.Order[Meat] = scalaz.Order$$anon$@7401c09f scala> Meat("Pork",) lt Meat("Pork",)
res14: Boolean = true scala> Meat("Beef",) gt Meat("Pork",)
res15: Boolean = false

3、逆变构建函数orderBy:

 scala> case class Money(amount: Int)
defined class Money scala> val moneyToInt: Money => Int = money => money.amount
moneyToInt: Money => Int = <function1> scala> implicit val moneyOrder: Order[Money] = Order.orderBy(moneyToInt)
moneyOrder: scalaz.Order[Money] = scalaz.Order$$anon$@3e3975d0 scala> Money() lt Money()
res16: Boolean = true scala> Money() ?|? Money()
res17: scalaz.Ordering = GT

在使用逆变构建函数时我们不需要再考虑如何实现对两个对象值的对比来获取这个Ordering返回值,我们只知道Order[Int]实现了两个Int的对比就行了。

Show 是一个简单的typeclass。我们用Shows(T)来实现对类型T的字符描述:

在scalaz/Syntax/ShowSyntax.scala里的注入方法:

 final class ShowOps[F] private[syntax](val self: F)(implicit val F: Show[F]) extends Ops[F] {
////
final def show: Cord = F.show(self)
final def shows: String = F.shows(self)
final def print: Unit = Console.print(shows)
final def println: Unit = Console.println(shows)
////
}

我们用Show来描述Person类型:

 scala> case class Person(name: String, age: Int)
defined class Person
scala> implicit val personShow: Show[Person] = Show.show {p => p.name + "," + p.age + " years old" }
personShow: scalaz.Show[Person] = scalaz.Show$$anon$@1d80fcd3
res19: String = Harry, years old scala> Person("Harry",).shows
res20: String = Harry, years old scala> Person("Harry",).println
Harry, years old

Enum typeclass 提供了下面这些方法:

 final class EnumOps[F] private[syntax](val self: F)(implicit val F: Enum[F]) extends Ops[F] {
////
final def succ: F =
F succ self final def -+-(n: Int): F =
F.succn(n, self) final def succx: Option[F] =
F.succx.apply(self) final def pred: F =
F pred self final def ---(n: Int): F =
F.predn(n, self) final def predx: Option[F] =
F.predx.apply(self) final def from: EphemeralStream[F] =
F.from(self) final def fromStep(step: Int): EphemeralStream[F] =
F.fromStep(step, self) final def |=>(to: F): EphemeralStream[F] =
F.fromTo(self, to) final def |->(to: F): List[F] =
F.fromToL(self, to) final def |==>(step: Int, to: F): EphemeralStream[F] =
F.fromStepTo(step, self, to) final def |-->(step: Int, to: F): List[F] =
F.fromStepToL(step, self, to) ////
}

下面是使用这些操作符号的例子:

 scala> 'a' to 'e'
res22: scala.collection.immutable.NumericRange.Inclusive[Char] = NumericRange(a, b, c, d, e) scala> 'a' |-> 'e'
res23: List[Char] = List(a, b, c, d, e) scala> 'a' |=> 'e'
res24: scalaz.EphemeralStream[Char] = scalaz.EphemeralStreamFunctions$$anon$@2f8a4dfd scala> 'a'.succ
res25: Char = b
scala> 'a' -+-
res26: Char = c scala> 'd' ---
res27: Char = b

Enum实例需要实现抽象函数succ,pred。下面是char裂隙Enum实例Enum[Char]的实现:在scalaz/std/AnyVal.scala里的char object

     def succ(b: Char) = (b + ).toChar
def pred(b: Char) = (b - ).toChar
override def succn(a: Int, b: Char) = (b + a).toChar
override def predn(a: Int, b: Char) = (b - a).toChar

再仔细看看Enum trait如下;

trait Enum[F] extends Order[F] { self =>
//// def succ(a: F): F
def pred(a: F): F

Enum实例必须实现抽象函数succ,pred。除此之外由于Enum继承了Order,所以还必须实现Order trait的抽象函数order(a1,a2)。

												

Scalaz(4)- typeclass:标准类型-Equal,Order,Show,Enum的更多相关文章

  1. 标准类型String(学习中)

    1.读取string对象 #include<iostream> #include<cstring> using namespace std; int main() { stri ...

  2. 标准类型内建函数 cmp()介绍

    内建函数cmp()用于比较两个对象obj1 和obj2, 如果obj1 小于obj2, 则返回一个负整数,如果obj1 大于obj2 则返回一个正整数, 如果obj1 等于obj2, 则返回0.它的行 ...

  3. day2_python学习笔记_chapter4_标准类型和内建函数

    1. 标准类型 Integer,Boolean, Long integer, Floating point real number, Complex number, String, List, Tup ...

  4. python基础----isinstance(obj,cls)和issubclass(sub,super)、反射、__setattr__,__delattr__,__getattr__、二次加工标准类型(包装)

    一.isinstance(obj,cls)和issubclass(sub,super)                                isinstance(obj,cls)检查是否ob ...

  5. python基础之类的内置__setattr__,__delattr__,__getattr__和 二次加工标准类型(包装)

    一.内置attr:__setattr__,__delattr__,__getattr__ __setattr__ #添加/修改属性会触发它的执行 __delattr__ #删除属性的时候会触发 __g ...

  6. Effective JavaScript Item 40 避免继承标准类型

    本系列作为Effective JavaScript的读书笔记. ECMAScript标准库不大.可是提供了一些重要的类型如Array,Function和Date.在一些场合下.你或许会考虑继承当中的某 ...

  7. Python学习15之python内置六大标准类型

    1.六大标准类型:数值型,str,list,set,tuple,dic 2.数值型:int,float,bool,complex 3.区别: 1)数值型和str,tuple都是不可变类型 而list, ...

  8. Mybatis中使用自定义的类型处理器处理枚举enum类型

    知识点:在使用Mybatis的框架中,使用自定义的类型处理器处理枚举enum类型 应用:利用枚举类,处理字段有限,可以用状态码,代替的字段,本实例,给员工状态字段设置了一个枚举类 状态码,直接赋值给对 ...

  9. 标准类型内建函数 type()介绍

    我们现在来正式介绍一下 type().在Python2.2 以前, type() 是内建函数.不过从那时起,它变成了一个“工厂函数”.在本章的后面部分我们会讨论工厂函数, 现在你仍然可以将type() ...

随机推荐

  1. SQL中group by的用法

    group by即按照给定字段对结果集进行分组,从字面意义上理解就是根据"by"指定的规则对数据进行分组,所谓的分组就是将一个"数据集"划分成若干个" ...

  2. Atitit RSA非对称加密原理与解决方案

    Atitit RSA非对称加密原理与解决方案 1.1. 一.一点历史 1 1.2. 八.加密和解密 2 1.3. 二.基于RSA的消息传递机制  3 1.4. 基于rsa的授权验证机器码 4 1.5. ...

  3. fir.im Weekly - iOS/Android 应用程序架构解析

    假如问你一个iOS or Android app的架构,你会从哪些方面来说呢? 本期 fir.im Weekly 收集了关于  iOS/Android 开发资源,也加入了一些关于 Web 前端方面的分 ...

  4. 11.按要求编写Java应用程序。 (1)创建一个叫做机动车的类: 属性:车牌号(String),车速(int),载重量(double) 功能:加速(车速自增)、减速(车速自减)、修改车牌号,查询车的载重量。 编写两个构造方法:一个没有形参,在方法中将车牌号设置“XX1234”,速 度设置为100,载重量设置为100;另 一个能为对象的所有属性赋值; (2)创建主类: 在主类中创建两个机动车对象。

    package java1; public class Che { //属性 public String nub; public int speed; public double weight ; C ...

  5. javase基础复习攻略《七》

    容器是什么?通俗的讲容器指可以装其它东西的器皿,前面我们提到的数组便是容器的一种,容器的概念在JAVA中便可以理解为用来存储其它对象的器皿.本篇就让我们一起来认识一下JAVA为我们提供的容器类. 1. ...

  6. java中同步嵌套引起的死锁事例代码

    /* 目的:自己写一个由于同步嵌套引起的死锁! 思路:多个线程在执行时,某一时刻,0-Thread绑定了LockA锁,1-Thread绑定了LockB锁! 当0-Thread要去绑定LockB锁时 和 ...

  7. Windows Azure Active Directory (1) 前言 - 基于声明的验证和授权

    <Windows Azure Platform 系列文章目录> 在我们介绍整套系统架构之前,我们需要首先定义一些基本的概念. 用户及其属性: 用户值得是要使用某项服务的个体.用户一般都有一 ...

  8. Windows Azure Active Directory (3) China Azure AD增加新用户

    <Windows Azure Platform 系列文章目录> 本文介绍的是国内由世纪互联运维的China Azure. 本文是对笔者之前的文档:Windows Azure Active ...

  9. Laravel4中的Validator

    不管写接口还是写web页面,实质都是传入参数,然后进行业务逻辑,然后再输出具体内容.所以,对参数的验证是不可避免的一个环节,比如传过来的email是不是为空,是不是合法的email格式?laravel ...

  10. 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要一

    本人有幸在Learning Hard举行的整点抢书活动<Learninghard C#学习笔记>回馈网友,免费送书5本中免费获得了一本<LEARNING HARD C#学习笔记> ...