我正在读http://eed3si9n.com/learning-scalaz/a+Yes-No+typeclass.html,现在我读的是Yes-No type类的部分。最终的目标是让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
}
}
}在我看来是一样的。那么,为什么我们需要在教程中使用这种方式呢?我错过了什么?你能解释一下有什么区别吗?
发布于 2017-11-26 04:32:14
我认为这里的问题是误解了这句话的范围:
最终的目标是让
1.truthy返回true。
这就是我们试图对CanTruthyOps类做的事情,但它不是CanTruthy类型类的目标,更普遍的说,类似于这类的语法问题并不是类型类的目标。
类型类的目标是允许我们以简单、灵活、组合的方式约束类型。类型参数少的CanTruthy方法实际上并不能很好地支持简单的部分或灵活的部分或组合部分(可以说,Scala中的类型类的实现也不是很简单,但它至少更简单,而且肯定更灵活和更复杂)。
以教程中的这个方法为例(为了避免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但是现在假设您需要保留原始类型。这可能有很多原因--例如,在检查其中一个值的真实性之前,您可能希望使用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请注意,这些方法看起来几乎相同--在这两种方法中,都需要一些隐式证据(在编译时)来证明A是真实的。在类型参数少样式中,此证据是隐式转换,而在类型类样式中,则是泛型类型的隐式值。
后一种方法有几个优点。一种抽象之一是,它允许我们将“这是我知道如何对这种类型做X的一些证据”与纯粹的句法关系区分开来,我可以在这件事上调用.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
}
}作为Int => Addable隐式转换,没有一种很好的方法来完成这种事情。
类型类方法同样处理需要操作的多个类型的情况,而类型参数-less方法实际上并不是这样(至少不是以任何相当干净的方式)。
因此,总结一下:如果您只想要一些在有具体类型的情况下通常使用的很好的充实方法,那么类型参数少的方法是完全合理的,并且可能需要更少的代码。如果您希望能够以高效、灵活、通用和合理优雅的方式抽象支持某些操作的类型,请编写一个类型类。
https://stackoverflow.com/questions/47357537
复制相似问题