# Scalaz（4）－ typeclass：标准类型－Equal,Order,Show,Enum

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

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

1、特质 trait

2、隐式实例 implicit instances

3、方法注入 method injection

Equal Trait 在 core/.../scalaz/Equal.scala里，比较简单：

1 trait Equal[F] { self =>

2 ////

3 def equal(a1: F, a2: F): Boolean

4

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

6 def equal(a1: G, a2: G) = self.equal(f(a1), f(a2))

7 }

8

9 /** @return true, if `equal(f1, f2)` is known to be equivalent to `f1 == f2` */

10 def equalIsNatural: Boolean = false

11 ...

1 cala> 2 == 2.0

2 res3: Boolean = true

3

4 scala> 2 === 2.0

5 <console>:14: error: type mismatch;

6 found : Double(2.0)

7 required: Int

8 2 === 2.0

9 ^

1 implicit val intInstance: Monoid[Int] with Enum[Int] with Show[Int] = new Monoid[Int] with Enum[Int] with Show[Int] {

2 override def shows(f: Int) = f.toString

3

4 def append(f1: Int, f2: => Int) = f1 + f2

5

6 def zero: Int = 0

7

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

9

10 def succ(b: Int) = b + 1

11 def pred(b: Int) = b - 1

12 override def succn(a: Int, b: Int) = b + a

13 override def predn(a: Int, b: Int) = b - a

14 override def min = Some(Int.MinValue)

15 override def max = Some(Int.MaxValue)

16

17 override def equalIsNatural: Boolean = true

18 }

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

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

1 trait Order[F] extends Equal[F] { self =>

2 ////

3 def apply(x: F, y: F): Ordering = order(x, y)

4

5 def order(x: F, y: F): Ordering

6

7 def equal(x: F, y: F): Boolean = order(x, y) == Ordering.EQ

1 /** Wraps a value `self` and provides methods related to `Equal` */

2 final class EqualOps[F] private[syntax](val self: F)(implicit val F: Equal[F]) extends Ops[F] {

3 ////

4

5 final def ===(other: F): Boolean = F.equal(self, other)

6 final def /==(other: F): Boolean = !F.equal(self, other)

7 final def =/=(other: F): Boolean = /==(other)

8 final def ≟(other: F): Boolean = F.equal(self, other)

9 final def ≠(other: F): Boolean = !F.equal(self, other)

10

11 /** Raises an exception unless self === other. */

12 final def assert_===[B](other: B)(implicit S: Show[F], ev: B <:< F) =

13 if (/==(other)) sys.error(S.shows(self) + "" + S.shows(ev(other)))

14

15 ////

16 }

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

1 trait ToEqualOps {

2 implicit def ToEqualOps[F](v: F)(implicit F0: Equal[F]) =

3 new EqualOps[F](v)

4

5 ////

6

7 ////

8 }

1 trait ToTypeClassOps

2 extends ToSemigroupOps with ToMonoidOps with ToEqualOps with ToShowOps

3 with ToOrderOps with ToEnumOps with ToPlusEmptyOps

4 with ToFunctorOps with ToContravariantOps with ToApplyOps

6 with ToBifoldableOps with ToCozipOps

7 with ToPlusOps with ToApplicativePlusOps with ToMonadPlusOps with ToTraverseOps with ToBifunctorOps

8 with ToBitraverseOps with ToComposeOps with ToCategoryOps

9 with ToArrowOps with ToFoldableOps with ToChoiceOps with ToSplitOps with ToZipOps with ToUnzipOps with ToMonadTellOps with ToMonadListenOps with ToMonadErrorOps

10 with ToFoldable1Ops with ToTraverse1Ops with ToOptionalOps with ToCatchableOps with ToAlignOps

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

1 object Scalaz

2 extends StateFunctions // Functions related to the state monad

3 with syntax.ToTypeClassOps // syntax associated with type classes

4 with syntax.ToDataOps // syntax associated with Scalaz data structures

5 with std.AllInstances // Type class instances for the standard library types

6 with std.AllFunctions // Functions related to standard library types

7 with syntax.std.ToAllStdOps // syntax associated with standard library types

8 with IdInstances // Identity type and instances

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

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

2 def equal(a1: A, a2: A) = f(a1, a2)

3 }

1 scala> case class Person(name: String, age: Int)

2 defined class Person

3 scala> implicit val personEqual: Equal[Person] = Equal.equal{(a,b) => a.name == b.name && a.age == b.age}

4 personEqual: scalaz.Equal[Person] = scalaz.Equal\$\$anon\$7@7e5716e

5

6 scala> Person("Jone",23) === Person("Jone",23)

7 res0: Boolean = true

8

9 scala> Person("Jone",23) === Person("Jone",22)

10 res1: Boolean = false

11

12 scala> Person("Jone",23) === Person("John",23)

13 res2: Boolean = false

1 scala> implicit val personEqual = new Equal[Person] {

2 | def equal(a1: Person, a2: Person): Boolean = a1.name == a2.name && a1.age == a2.age

3 | }

4 personEqual: scalaz.Equal[Person] = \$anon\$1@247cc8f

5

6 scala> Person("John",32) === Person("Joe",32)

7 res0: Boolean = false

8

9 scala> Person("John",32) === Person("John",32)

10 res1: Boolean = true

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

2 def equal(a1: G, a2: G) = self.equal(f(a1), f(a2))

3 }

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

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

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

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

1 scala> case class MoneyCents(cents: Int)

2 defined class MoneyCents

3 scala> def moneyToInt(m: MoneyCents): Int = m.cents * 100

4 moneyToInt: (m: MoneyCents)Int

5

6 scala> implicit val moneyEqual: Equal[MoneyCents] = Equal.equalBy(moneyToInt)

8

9 scala> MoneyCents(120) === MoneyCents(120)

10 res2: Boolean = true

11

12 scala> MoneyCents(122) === MoneyCents(120)

13 res3: Boolean = false

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

1 /** Wraps a value `self` and provides methods related to `Order` */

2 final class OrderOps[F] private[syntax](val self: F)(implicit val F: Order[F]) extends Ops[F] {

3 ////

4 final def <(other: F): Boolean = F.lessThan(self, other)

5 final def <=(other: F): Boolean = F.lessThanOrEqual(self, other)

6 final def >(other: F): Boolean = F.greaterThan(self, other)

7 final def >=(other: F): Boolean = F.greaterThanOrEqual(self, other)

8 final def max(other: F): F = F.max(self, other)

9 final def min(other: F): F = F.min(self, other)

10 final def cmp(other: F): Ordering = F.order(self, other)

11 final def ?|?(other: F): Ordering = F.order(self, other)

12 final def lte(other: F): Boolean = F.lessThanOrEqual(self, other)

13 final def gte(other: F): Boolean = F.greaterThanOrEqual(self, other)

14 final def lt(other: F): Boolean = F.lessThan(self, other)

15 final def gt(other: F): Boolean = F.greaterThan(self, other)

16 ////

17 }

1 object Ordering extends OrderingInstances with OrderingFunctions {

2 case object LT extends Ordering(-1, "LT") { def complement = GT }

3 case object EQ extends Ordering(0, "EQ") { def complement = EQ }

4 case object GT extends Ordering(1, "GT") { def complement = LT }

5 }

1 scala> 1 < 1.0

2 res4: Boolean = false

3

4 scala> 1 lt 1.0

5 <console>:21: error: type mismatch;

6 found : Double(1.0)

7 required: Int

8 1 lt 1.0

9 ^

10

11 scala> 1 ?|? 1.0

12 <console>:21: error: type mismatch;

13 found : Double(1.0)

14 required: Int

15 1 ?|? 1.0

16 ^

17

18 scala> 1 ?|? 2

19 res7: scalaz.Ordering = LT

20

21 scala> 1 lt 2

22 res8: Boolean = true

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

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

1 scala> case class Person(name: String, age: Int)

2 defined class Person

3

4 scala> implicit val personAgeOrder = new Order[Person] {

5 | def order(a1: Person, a2: Person): Ordering =

6 | if (a1.age < a2.age) Ordering.LT else if (a1.age > a2.age) Ordering.GT else Ordering.EQ

7 | }

8 personAgeOrder: scalaz.Order[Person] = \$anon\$1@736d65e9

9 scala> Person("John",23) ?|? Person("Joe",24)

10 res11: scalaz.Ordering = LT

11

12 scala> Person("John",23) lt Person("Joe",24)

13 res12: Boolean = true

14

15 scala> Person("John",23) gt Person("Joe",24)

16 res13: Boolean = false

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

1 scala> case class Meat(cat: String, weight: Int)

2 defined class Meat

3 scala> implicit val meatWeightOrder: Order[Meat] = Order.order(_.weight ?|? _.weight)

4 meatWeightOrder: scalaz.Order[Meat] = scalaz.Order\$\$anon\$11@7401c09f

5

6 scala> Meat("Pork",13) lt Meat("Pork",14)

7 res14: Boolean = true

8

9 scala> Meat("Beef",13) gt Meat("Pork",14)

10 res15: Boolean = false

3、逆变构建函数orderBy:

1 scala> case class Money(amount: Int)

2 defined class Money

3

4 scala> val moneyToInt: Money => Int = money => money.amount

5 moneyToInt: Money => Int = <function1>

6

7 scala> implicit val moneyOrder: Order[Money] = Order.orderBy(moneyToInt)

8 moneyOrder: scalaz.Order[Money] = scalaz.Order\$\$anon\$7@3e3975d0

9

10 scala> Money(20) lt Money(21)

11 res16: Boolean = true

12

13 scala> Money(20) ?|? Money(12)

14 res17: scalaz.Ordering = GT

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

1 final class ShowOps[F] private[syntax](val self: F)(implicit val F: Show[F]) extends Ops[F] {

2 ////

3 final def show: Cord = F.show(self)

4 final def shows: String = F.shows(self)

5 final def print: Unit = Console.print(shows)

6 final def println: Unit = Console.println(shows)

7 ////

8 }

1 scala> case class Person(name: String, age: Int)

2 defined class Person

3 scala> implicit val personShow: Show[Person] = Show.show {p => p.name + "," + p.age + " years old" }

4 personShow: scalaz.Show[Person] = scalaz.Show\$\$anon\$4@1d80fcd3

5 res19: String = Harry,24 years old

6

7 scala> Person("Harry",24).shows

8 res20: String = Harry,24 years old

9

10 scala> Person("Harry",24).println

11 Harry,24 years old

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

1 final class EnumOps[F] private[syntax](val self: F)(implicit val F: Enum[F]) extends Ops[F] {

2 ////

3 final def succ: F =

4 F succ self

5

6 final def -+-(n: Int): F =

7 F.succn(n, self)

8

9 final def succx: Option[F] =

10 F.succx.apply(self)

11

12 final def pred: F =

13 F pred self

14

15 final def ---(n: Int): F =

16 F.predn(n, self)

17

18 final def predx: Option[F] =

19 F.predx.apply(self)

20

21 final def from: EphemeralStream[F] =

22 F.from(self)

23

24 final def fromStep(step: Int): EphemeralStream[F] =

25 F.fromStep(step, self)

26

27 final def |=>(to: F): EphemeralStream[F] =

28 F.fromTo(self, to)

29

30 final def |->(to: F): List[F] =

31 F.fromToL(self, to)

32

33 final def |==>(step: Int, to: F): EphemeralStream[F] =

34 F.fromStepTo(step, self, to)

35

36 final def |-->(step: Int, to: F): List[F] =

37 F.fromStepToL(step, self, to)

38

39 ////

40 }

1 scala> 'a' to 'e'

2 res22: scala.collection.immutable.NumericRange.Inclusive[Char] = NumericRange(a, b, c, d, e)

3

4 scala> 'a' |-> 'e'

5 res23: List[Char] = List(a, b, c, d, e)

6

7 scala> 'a' |=> 'e'

8 res24: scalaz.EphemeralStream[Char] = scalaz.EphemeralStreamFunctions\$\$anon\$4@2f8a4dfd

9

10 scala> 'a'.succ

11 res25: Char = b

12 scala> 'a' -+- 2

13 res26: Char = c

14

15 scala> 'd' --- 2

16 res27: Char = b

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

1 def succ(b: Char) = (b + 1).toChar

2 def pred(b: Char) = (b - 1).toChar

3 override def succn(a: Int, b: Char) = (b + a).toChar

4 override def predn(a: Int, b: Char) = (b - a).toChar

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)。

Top