我正在阅读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
}
}
}
对我来说也一样。那么为什么我们需要在教程中使用这种方式呢?我错过了什么样的东西?你能解释一下有什么区别吗?
答案 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
}
}
隐式转换,做这种事情的方法并不好。
类型类方法类似地处理您需要操作等多种类型的情况,等等,而类型无参数方法并非如此(至少没有以任何合理清洁的方式)
总结一下:如果你只是想要一些很好的浓缩方法,而这些方法通常在你有具体类型的情况下使用,那么类型无参数方法是完全合理的,并且可能涉及的代码少一些。如果您希望能够以高效,灵活,通用且相当优雅的方式抽象支持某些操作的类型,请编写类型类。