# 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 = false11 ...`

`1 cala> 2 == 2.02 res3: Boolean = true34 scala> 2 === 2.05 <console>:14: error: type mismatch;6 found : Double(2.0)7  required: Int8 2 === 2.09 ^`

` 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 910 def succ(b: Int) = b + 111 def pred(b: Int) = b - 112 override def succn(a: Int, b: Int) = b + a13 override def predn(a: Int, b: Int) = b - a14 override def min = Some(Int.MinValue)15 override def max = Some(Int.MaxValue)1617 override def equalIsNatural: Boolean = true18 }`

`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)45  def order(x: F, y: F): Ordering67 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)1011 /** 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)))1415 ////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)45 ////67 ////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 5  with ToApplicativeOps with ToBindOps with ToMonadOps with ToComonadOps 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 ToMonadErrorOps10 with ToFoldable1Ops with ToTraverse1Ops with ToOptionalOps with ToCatchableOps with ToAlignOps`

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

`1 object Scalaz2 extends StateFunctions // Functions related to the state monad3 with syntax.ToTypeClassOps // syntax associated with type classes4 with syntax.ToDataOps // syntax associated with Scalaz data structures5 with std.AllInstances // Type class instances for the standard library types6 with std.AllFunctions // Functions related to standard library types7 with syntax.std.ToAllStdOps // syntax associated with standard library types8 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 = false1112 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) 7 moneyEqual: scalaz.Equal[MoneyCents] = scalaz.Order\$\$anon\$7@138ad7f5 8 9 scala> MoneyCents(120) === MoneyCents(120)10 res2: Boolean = true1112 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 ^1011 scala> 1 ?|? 1.012 <console>:21: error: type mismatch;13 found : Double(1.0)14  required: Int15 1 ?|? 1.016 ^1718 scala> 1 ?|? 219 res7: scalaz.Ordering = LT2021 scala> 1 lt 222 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 = LT1112 scala> Person("John",23) lt Person("Joe",24)13 res12: Boolean = true1415 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 910 scala> Money(20) lt Money(21)11 res16: Boolean = true1213 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 910 scala> Person("Harry",24).println11 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)1112 final def pred: F =13  F pred self1415 final def ---(n: Int): F =16  F.predn(n, self)1718 final def predx: Option[F] =19  F.predx.apply(self)2021 final def from: EphemeralStream[F] =22 F.from(self)2324 final def fromStep(step: Int): EphemeralStream[F] =25  F.fromStep(step, self)2627 final def |=>(to: F): EphemeralStream[F] =28  F.fromTo(self, to)2930 final def |->(to: F): List[F] =31  F.fromToL(self, to)3233 final def |==>(step: Int, to: F): EphemeralStream[F] =34  F.fromStepTo(step, self, to)3536 final def |-->(step: Int, to: F): List[F] =37  F.fromStepToL(step, self, to)3839 ////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 910 scala> 'a'.succ11 res25: Char = b12 scala> 'a' -+- 213 res26: Char = c1415 scala> 'd' --- 216 res27: Char = b`

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

`1 def succ(b: Char) = (b + 1).toChar2 def pred(b: Char) = (b - 1).toChar3 override def succn(a: Int, b: Char) = (b + a).toChar4 override def predn(a: Int, b: Char) = (b - a).toChar`

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

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