Scala-with-cats中文翻译(二):初遇Cats:Show type class

#scala #cats #type class

前言

本篇内容主要来学习Cats中一个基础的type class:Show。

学习程度:需要完全掌握

1.4 初遇 Cats

在先前的章节我们学习了如何在Scala中去实现一个type class,在本节中我们学习Cats中已经实现的type class。

Cats是的设计是模块化,我们可以自由选择自己想要的type class、instance、interface methods。让我们来看第一个例子:cats.Show

Show的功能跟我们在上节实现的Printable基本一致。它的主要功能就是帮助我们将数据以更友好的方式输出的控制台,而不是通过toString方法,下面是它的一个简单声明:

package cats
trait Show[A] {
  def show(value: A): String
}

1.4.1 导入Type Class

Show这个type class声明在cats这个包里,我们可以直接进行import:

import cats.Show

在Cats中,每个type class的伴生对象中都有一个apply方法,用于查找对应类型的instance:

val showInt = Show.apply[Int]
// <console>:13: error: could not find implicit value for parameter
//  instance: cats.Show[Int]
// val showInt = Show.apply[Int]

糟糕,竟然报错了,因为apply方法是通过implicit来查找对应的instance,所以我们需要导入相应的instance到implicit scope。

1.4.2 导入默认的Instances实现

cats.instances这个包提供了很多默认实现的instance,我们可以通过以下方式来导入它们,每种类型的包都包含了该类型对于Cats中所有type class的instance实现:

有关可用导入的详细信息,请参见cats.instances包。

让我们来导入Int和String对应Show的instances:

import cats.instances.int._    // for Show
import cats.instances.string._ // for Show

val showInt: Show[Int] = Show.apply[Int] 
val showString: Show[String] = Show.apply[String]

很好,我们导入了Int和String对应Show的instance,现在可以使用它们来打印Int和String的数据:

val intAsString: String = showInt.show(123)
// intAsString: String = 123

val stringAsString: String = showString.show("abc")
// stringAsString: String = abc

1.4.3 导入Interface Syntax

我们可以使用interface syntax让Show变的更容易使用,首先我们需要先导入cats.syntax.show,它会为任意类型添加一个show的扩展方法,前提是implicit scope已经有了对应类型的instance:

import cats.syntax.show._ // for show

val shownInt = 123.show
// shownInt: String = 123

val shownString = "abc".show
// shownString: String = abc

Cats为每一个type class都提供了syntax,我们可以按需使用,在后面的章节,我们会继续介绍它们。

1.4.4 导入所有内容

在这本书中,我们对于每个示例都是按需导入,只导入需要的instance和syntax。然而,有些时候这也是相当费时的,你可以通过以下方式简化导入:

  • import cats._ 导入Cats中所有的type class
  • import cats.instances.all._ 导入Cats中所有的instances
  • import cats.syntax.all._ 导入Cats中所有的syntax
  • import cats.implicits._ 导入Cats中所有的instances和syntax

大多数时候我们只需要全部导入即可:

import cats._
import cats.implicits._

但当遇到命名冲突或者implicit冲突的时候,我们就需要更具体导入。

1.4.5 实现一个自定义的Instance

下面我们来实现Date类型对应Show的instance:

import java.util.Date

implicit val dateShow: Show[Date] =
  new Show[Date] {
    def show(date: Date): String =
      s"${date.getTime}ms since the epoch."
}

同时,Cats提供了一些更简洁的方法去创建instance。对于Show来说,在其伴生对象中有两个方法帮助我们创建自定义类型的instance:

object Show {
  
  // Convert a function to a `Show` instance:
  def show[A](f: A => String): Show[A] = ???
  
  // Create a `Show` instance from a `toString` method:
  def fromToString[A]: Show[A] = ???
}

使用这些方法会比传统创建instance的方式更加快速:

implicit val dateShow: Show[Date] = Show.show(date => s"${date.getTime}ms since the epoch.")

我们可以看到,确实简洁了不少,Cats为很多type class都提供了类似的辅助方法来创建instance,可以从头直接创建instance,也可以基于其他类型的instance创建新的instance,比如:基于Int类型的instance创建Option[Int]类型的instance。

1.4.6 练习: 使用Cat Show

使用Show type class重写上面章节Printable的例子,代码见示例

文章允许转载、使用,但需要保留链接文章署名 godpan.me,如有写的不当之处,也欢迎大家指正,联系邮箱:godpan.sen@gmail.com