如何匹配没有特定类型类实例的类型?
How do I match types that don't have a specific typeclass instance?
我想为那些没有特定类型类实例的类型定义一些行为:
// Given
trait SomeTypeclass[T]
// when we have implicit SomeTypeclass[T]
def f[T: SomeTypeclass](x:T):Unit = ???
// when we don't have instance
def f[T !: SomeTypeclass](x: T):Unit = ???
我们可以处理类型类中的差异,但我需要创建额外的实例来支持一些通用行为。
有没有一种方法可以否定类型绑定? 一种使函数具有 !:
编译的方法?
(我想在没有 scalaz、shapeless 等的 vanilla Scala 中这样做)
Is there a way to negate a type bound?
不!语法 [T: SomeTypeclass]
对于 (implicit val t: Sometypeclass[T])
只是 shorthand,并且没有办法 "negate"。您可以重载该方法,但这会产生歧义。
但是你可以"nest"输入classes。
trait ExistsLowPri {
implicit def no[A]: Exists[A] = Exists.No
}
object Exists extends ExistsLowPri {
case class Yes[A](peer: A) extends Exists[A]
case object No extends Exists[Nothing]
implicit def yes[A](implicit peer: A): Exists[A] = new Yes(peer)
}
sealed trait Exists[+A]
示例:
trait Show[-A] {
def show(x: A): String
}
def test[A](x: A)(implicit ex: Exists[Show[A]]): Unit = println(
ex match {
case Exists.Yes(s) => s.show(x)
case Exists.No => "(no string repr)"
}
)
implicit object ShowBoolean extends Show[Boolean] {
def show(b: Boolean) = if (b) "T" else "F"
}
test(123) // (no string repr)
test(true) // T
但是,我强烈建议不要这样做,因为隐式和类型 classes 的要点是,如果某些内容不在范围内,编译器会显式失败。这样您将始终能够编译但不能保证您正确地将特定类型 class 引入范围。
您可以自己滚动:
trait NoInstance[T[_], A]
object NoInstance extends LowPriorityNoInstance {
implicit def hasInstance0[T[_], A](implicit inst: T[A]): NoInstance[T, A] = ???
implicit def hasInstance1[T[_], A](implicit inst: T[A]): NoInstance[T, A] = ???
}
class LowPriorityNoInstance {
implicit def noInstance[T[_], A]: NoInstance[T, A] = new NoInstance[T, A] {}
}
然后:
scala> implicitly[NoInstance[Ordering, List[Int]]]
res4: NoInstance[Ordering,List[Int]] = LowPriorityNoInstance$$anon@5e1fc2aa
scala> implicitly[NoInstance[Ordering, Int]]
<console>:14: error: ambiguous implicit values:
both method hasInstance0 in object NoInstance of type [T[_], A](implicit inst: T[A])NoInstance[T,A]
and method hasInstance1 in object NoInstance of type [T[_], A](implicit inst: T[A])NoInstance[T,A]
match expected type NoInstance[Ordering,Int]
implicitly[NoInstance[Ordering, Int]]
^
在许多情况下,您可以使用 null
默认隐式参数技巧来避免这种情况,但是:
def f[T](x: T)(implicit stc: SomeTypeclass[T] = null): Unit = Option(stc) match {
case Some(instance) => // do something in the case where we have an instance
case None => // do something in the case where we don't have an instance
}
这感觉像是一个 hack,但它确实有效,而且人们一直在使用它。
我想为那些没有特定类型类实例的类型定义一些行为:
// Given
trait SomeTypeclass[T]
// when we have implicit SomeTypeclass[T]
def f[T: SomeTypeclass](x:T):Unit = ???
// when we don't have instance
def f[T !: SomeTypeclass](x: T):Unit = ???
我们可以处理类型类中的差异,但我需要创建额外的实例来支持一些通用行为。
有没有一种方法可以否定类型绑定? 一种使函数具有 !:
编译的方法?
(我想在没有 scalaz、shapeless 等的 vanilla Scala 中这样做)
Is there a way to negate a type bound?
不!语法 [T: SomeTypeclass]
对于 (implicit val t: Sometypeclass[T])
只是 shorthand,并且没有办法 "negate"。您可以重载该方法,但这会产生歧义。
但是你可以"nest"输入classes。
trait ExistsLowPri {
implicit def no[A]: Exists[A] = Exists.No
}
object Exists extends ExistsLowPri {
case class Yes[A](peer: A) extends Exists[A]
case object No extends Exists[Nothing]
implicit def yes[A](implicit peer: A): Exists[A] = new Yes(peer)
}
sealed trait Exists[+A]
示例:
trait Show[-A] {
def show(x: A): String
}
def test[A](x: A)(implicit ex: Exists[Show[A]]): Unit = println(
ex match {
case Exists.Yes(s) => s.show(x)
case Exists.No => "(no string repr)"
}
)
implicit object ShowBoolean extends Show[Boolean] {
def show(b: Boolean) = if (b) "T" else "F"
}
test(123) // (no string repr)
test(true) // T
但是,我强烈建议不要这样做,因为隐式和类型 classes 的要点是,如果某些内容不在范围内,编译器会显式失败。这样您将始终能够编译但不能保证您正确地将特定类型 class 引入范围。
您可以自己滚动:
trait NoInstance[T[_], A]
object NoInstance extends LowPriorityNoInstance {
implicit def hasInstance0[T[_], A](implicit inst: T[A]): NoInstance[T, A] = ???
implicit def hasInstance1[T[_], A](implicit inst: T[A]): NoInstance[T, A] = ???
}
class LowPriorityNoInstance {
implicit def noInstance[T[_], A]: NoInstance[T, A] = new NoInstance[T, A] {}
}
然后:
scala> implicitly[NoInstance[Ordering, List[Int]]]
res4: NoInstance[Ordering,List[Int]] = LowPriorityNoInstance$$anon@5e1fc2aa
scala> implicitly[NoInstance[Ordering, Int]]
<console>:14: error: ambiguous implicit values:
both method hasInstance0 in object NoInstance of type [T[_], A](implicit inst: T[A])NoInstance[T,A]
and method hasInstance1 in object NoInstance of type [T[_], A](implicit inst: T[A])NoInstance[T,A]
match expected type NoInstance[Ordering,Int]
implicitly[NoInstance[Ordering, Int]]
^
在许多情况下,您可以使用 null
默认隐式参数技巧来避免这种情况,但是:
def f[T](x: T)(implicit stc: SomeTypeclass[T] = null): Unit = Option(stc) match {
case Some(instance) => // do something in the case where we have an instance
case None => // do something in the case where we don't have an instance
}
这感觉像是一个 hack,但它确实有效,而且人们一直在使用它。