ScalaZ中的TypeClasses

时间:2017-11-17 18:59:33

标签: scala typeclass scalaz

我正在阅读ScalaZ tutorial,现在我正在使用Yes-No类型的部分。最终目标是将1.truthy提升为return true。这是类型类的实现:

trait CanTruthy[A] { self =>
  /** @return true, if `a` is truthy. */
  def truthys(a: A): Boolean
}
object CanTruthy {
  def apply[A](implicit ev: CanTruthy[A]): CanTruthy[A] = ev
  def truthys[A](f: A => Boolean): CanTruthy[A] = new CanTruthy[A] {
    def truthys(a: A): Boolean = f(a)
  }
}
trait CanTruthyOps[A] {
  def self: A
  implicit def F: CanTruthy[A]
  final def truthy: Boolean = F.truthys(self)
}
object ToCanIsTruthyOps {
  implicit def toCanIsTruthyOps[A](v: A)(implicit ev: CanTruthy[A]) =
    new CanTruthyOps[A] {
      def self = v
      implicit def F: CanTruthy[A] = ev
    }
}

implicit val intCanTruthy: CanTruthy[Int] = CanTruthy.truthys({
         case 0 => false
         case _ => true
       })

对我来说有点吓人。我们引入了两个新的特征来实现这一目标。但是我们可以通过使用隐式类来实现相同的目标:

trait CanTruthy {
  def truthy: Boolean
}

object CanTruthy{
  implicit class CanTruthyInt(i: Int) extends CanTruthy{
    override def truthy: Boolean = i match {
      case 0 => false
      case _ => true
    }
  }
}

对我来说也一样。那么为什么我们需要在教程中使用这种方式呢?我错过了什么样的东西?你能解释一下有什么区别吗?

1 个答案:

答案 0 :(得分:1)

我认为这里的问题是误解了这句话的范围:

  

最终目标是让1.truthy返回true

这是我们尝试对CanTruthyOps内容进行的操作,但它不是 <{1}}类型的目标,并且更一般地说,这样的句法问题不是类型类的目标。

类型类的目标是允许我们以简单,灵活,组合的方式约束类型。类型无参数CanTruthy方法并不能很好地支持简单部分或灵活部分或组合部分(可以说Scala中类型类的实现也不是很简单,但是它至少有点简单,绝对更灵活和更具成分性。)

从教程中获取此方法,例如(轻微修改以避免CanTruthy):

Any

如果你想把它翻译成你的无类型参数样式,起初看起来很不错:

// Type class style
def truthyIf[A: CanTruthy, B](cond: A)(ifyes: => B)(ifno: => B): B =
  if (cond.truthy) ifyes else ifno

但现在假设你需要保持原始类型。这可能是必要的原因有很多 - 例如,在检查其中一个值的真实性之前,您可能希望使用// Parameterless style def truthyIf[B](cond: CanTruthy)(ifyes: => B)(ifno: => B): B = if (cond.truthy) ifyes else ifno 对值集合进行排序,或者您可能在此方法中使用此方法的变体type也是返回类型(在这里的类型类样式中):

scala.Ordering

现在翻译不那么有趣了:

// Type class style
def truthyOrElse[A: CanTruthy](cond: A)(ifno: => A): A =
  if (cond.truthy) cond else ifno

时髦的// Type parameter-less style def truthyOrElse[A <% CanTruthy](cond: A)(ifno: => A): A = if (cond.truthy) ifyes else ifno 事物是隐含参数的语法糖:

<%

但是类型类样式中的// Type parameter-less style (desugared) def truthyOrElse[A](cond: A)(ifno: => A)(implicit evidence$1: A => CanTruthy): A = if (cond.truthy) cond else ifno 也是语法糖:

:

请注意,这些方法看起来几乎相同 - 在您编写需要一些隐式证据的方法时(在编译时),// Type class style, desugared def truthyOrElse[A](cond: A)(ifno: => A)(implicit evidence$2: CanTruthy[A]): A = if (cond.truthy) cond else ifno 是真实的。在类型无参数样式中,此证据是隐式转换,而在类型类样式中,它是泛型类型的隐式值。

后一种方法有几个优点。一种抽象的方法是,它允许我们将这些类型的一些证据分开,这里我知道如何为这种类型做出X&#34;从纯粹的语法中得到关注&#34;我可以在这件事上打电话给A&#34;关心。当然,这种分离需要一些额外的机制(两个特征而不是一个),但在句法和语义问题之间保持一条清晰的界限是值得的。

另一个(相关的)优点是类型类可以更高效,因为它允许我们放弃语法,因此它还涉及额外的分配:

.x

如果您尝试提供证据的操作涉及多个值,则会出现另一个优势:

// Type class style, no syntax
def truthyOrElse[A](cond: A)(ifno: => A)(implicit ev: CanTruthy[A]): A =
  if (ev.truthys(cond)) cond else ifno

作为trait Addable[A] { def plus(a: A, b: A): A } object Addable { implicit val intAddable: Addable[Int] = new Addable[Int] { def plus(a: Int, b: Int): Int = a + b } } 隐式转换,做这种事情的方法并不好。

类型类方法类似地处理您需要操作等多种类型的情况,等等,而类型无参数方法并非如此(至少没有以任何合理清洁的方式)

总结一下:如果你只是想要一些很好的浓缩方法,而这些方法通常在你有具体类型的情况下使用,那么类型无参数方法是完全合理的,并且可能涉及的代码少一些。如果您希望能够以高效,灵活,通用且相当优雅的方式抽象支持某些操作的类型,请编写类型类。