使用上下文范围“负面”,以确保类型的类的实例是从范围缺席(Using context bounds

2019-08-22 04:08发布

TL;博士 :我怎样做类似下面的由代码:

def notFunctor[M[_] : Not[Functor]](m: M[_]) = s"$m is not a functor"

在“ Not[Functor] ”,正在这里组成部分。
我希望它成功时所提供的“M”是不是一个函子,否则失败的编译器。

解决 :跳过这个问题的其余部分,正前方去下面的答案。


我试图做到的是,粗略地说,“反面证据”。

伪代码会看起来像这样:

// type class for obtaining serialization size in bytes.
trait SizeOf[A] { def sizeOf(a: A): Long }

// type class specialized for types whose size may vary between instances
trait VarSizeOf[A] extends SizeOf[A]

// type class specialized for types whose elements share the same size (e.g. Int)
trait FixedSizeOf[A] extends SizeOf[A] {
  def fixedSize: Long
  def sizeOf(a: A) = fixedSize
}

// SizeOf for container with fixed-sized elements and Length (using scalaz.Length)
implicit def fixedSizeOf[T[_] : Length, A : FixedSizeOf] = new VarSizeOf[T[A]] {
  def sizeOf(as: T[A]) = ... // length(as) * sizeOf[A]
}

// SizeOf for container with scalaz.Foldable, and elements with VarSizeOf
implicit def foldSizeOf[T[_] : Foldable, A : SizeOf] = new VarSizeOf[T[A]] {
  def sizeOf(as: T[A]) = ... // foldMap(a => sizeOf(a))
}

请记住, fixedSizeOf()是优选的,其中重要,因为它为我们节省了集合的遍历。

这样一来,对于只有容器类型Length被定义(但不Foldable ),以及对于在其中一个元素FixedSizeOf定义,我们可以得到更好的性能。

对于其余情形下,我们去了收集和总结各个大小。

我的问题是在两种情况下LengthFoldable被用于容器限定,并且FixedSizeOf针对元件限定。 这是在这里一个非常普遍的情况下(例如,: List[Int]已两者定义)。

例:

scala> implicitly[SizeOf[List[Int]]].sizeOf(List(1,2,3))
<console>:24: error: ambiguous implicit values:
 both method foldSizeOf of type [T[_], A](implicit evidence$1: scalaz.Foldable[T], implicit evidence$2: SizeOf[A])VarSizeOf[T[A]]
 and method fixedSizeOf of type [T[_], A](implicit evidence$1: scalaz.Length[T], implicit evidence$2: FixedSizeOf[A])VarSizeOf[T[A]]
 match expected type SizeOf[List[Int]]
              implicitly[SizeOf[List[Int]]].sizeOf(List(1,2,3))

我想是能够依靠Foldable型类仅在Length + FixedSizeOf组合不适用。

为此,我可以改变的定义foldSizeOf()接受VarSizeOf元素:

implicit def foldSizeOfVar[T[_] : Foldable, A : VarSizeOf] = // ...

现在,我们必须填写覆盖问题部分Foldable与容器FixedSizeOf元素, 没有Length定义 。 我不知道如何处理这一点,但伪代码看起来是这样的:

implicit def foldSizeOfFixed[T[_] : Foldable : Not[Length], A : FixedSizeOf] = // ...

在“ Not[Length] ”,很明显,正在这里组成部分。

部分解决方案我知道

1)定义的低优先级implicits一个类和扩展它,如在“看到的object Predef extends LowPriorityImplicits ”。 最后隐式的( foldSizeOfFixed()可在父类中定义,并且将通过从子类替代被覆盖。

我不是在这个选项感兴趣,因为我希望最终能够支持递归使用SizeOf ,这将防止在低优先级的基类隐含从依靠那些在子类(在这里我的理解是否正确?编辑:!错隐查找从子类的情况下工作,这是一个可行的解决方案)!

2)更粗糙的方法是依赖于Option[TypeClass] (例如,: Option[Length[List]]少数那些和I可以只写一个大醇”隐式,其拾取FoldableSizeOf为强制性的和LengthFixedSizeOf作为。可选的,并依赖于后者(如果可用)(来源: 点击这里 )

这里的两个问题是缺乏模块化和回落至运行时异常时没有相关类型的类的实例可以定位(这个例子或许可以进行此解决方案的工作,但是这并不总是可能的)

编辑:这是我能得到可选implicits最好的。 这还没有实现:

implicit def optionalTypeClass[TC](implicit tc: TC = null) = Option(tc)
type OptionalLength[T[_]] = Option[Length[T]]
type OptionalFixedSizeOf[T[_]] = Option[FixedSizeOf[T]]

implicit def sizeOfContainer[
    T[_] : Foldable : OptionalLength,
    A : SizeOf : OptionalFixedSizeOf]: SizeOf[T[A]] = new SizeOf[T[A]] {
  def sizeOf(as: T[A]) = {

    // optionally calculate using Length + FixedSizeOf is possible
    val fixedLength = for {
      lengthOf <- implicitly[OptionalLength[T]]
      sizeOf <- implicitly[OptionalFixedSizeOf[A]]
    } yield lengthOf.length(as) * sizeOf.fixedSize

    // otherwise fall back to Foldable
    fixedLength.getOrElse { 
      val foldable = implicitly[Foldable[T]]
      val sizeOf = implicitly[SizeOf[A]]
      foldable.foldMap(as)(a => sizeOf.sizeOf(a))
    }
  }
}

除了这与碰撞fixedSizeOf()从早期的,这仍然是必要的。

感谢您的帮助或透视:-)

Answer 1:

我终于解决了这个使用基于模糊性的解决方案,不需要使用继承优先。

这是我在推广这样的尝试。

我们使用的类型Not[A]构建负型类:

import scala.language.higherKinds

trait Not[A]

trait Monoid[_] // or import scalaz._, Scalaz._
type NotMonoid[A] = Not[Monoid[A]] 

trait Functor[_[_]] // or import scalaz._, Scalaz._
type NotFunctor[M[_]] = Not[Functor[M]]

......然后可以用作上下文范围:

def foo[T: NotMonoid] = ...

我们继续通过确保不是每一个有效的表达[A]将获得至少一个隐式实例。

implicit def notA[A, TC[_]] = new Not[TC[A]] {}

实例被称为“诺塔” - “不”,因为如果它是发现的唯一实例“不[TC [A]”,那么负型类中找到适用; 在“A”通常为附加与扁平形状的类型(例如强度)处理的方法。

我们现在推出的歧义转走其中施加不需要的类型类情况:

implicit def notNotA[A : TC, TC[_]] = new Not[TC[A]] {}

这几乎是完全一样的“诺塔”,但这里我们只针对由“TC”指定类型的类的实例中隐含的范围存在类型感兴趣。 实例被命名为“notNotA”,因为仅仅通过匹配隐含进行查找,它会创建一个“诺塔”歧义,没有隐式搜索(这是我们的目标)。

让我们在使用示例。 我们将使用“NotMonoid”被动型类从上面:

implicitly[NotMonoid[java.io.File]] // succeeds
implicitly[NotMonoid[Int]] // fails

def showIfNotMonoid[A: NotMonoid](a: A) = a.toString

showIfNotMonoid(3) // fails, good!
showIfNotMonoid(scala.Console) // succeeds for anything that isn't a Monoid

到现在为止还挺好! 然而,类型形状的M [_]和类型类形TC [_ [_]]尚未通过上述方案支持。 让我们添加implicits他们还有:

implicit def notM[M[_], TC[_[_]]] = new Not[TC[M]] {}
implicit def notNotM[M[_] : TC, TC[_[_]]] = new Not[TC[M]] {}

implicitly[NotFunctor[List]] // fails
implicitly[NotFunctor[Class]] // succeeds

够简单。 需要注意的是Scalaz有一种变通方法从处理若干类型的形状产生的样板 - 寻找“取消应用”。 我一直没能利用它的基本情况(类型的类形的TC [_],如含半幺群),即使它的工作在TC [_ [_](如函子)就像一个魅力,所以这个答案并不覆盖。

如果任何人的兴趣,这里是在一个单一的代码段所需要的一切:

import scala.language.higherKinds

trait Not[A]

object Not {
  implicit def notA[A, TC[_]] = new Not[TC[A]] {}
  implicit def notNotA[A : TC, TC[_]] = new Not[TC[A]] {}

  implicit def notM[M[_], TC[_[_]]] = new Not[TC[M]] {}
  implicit def notNotM[M[_] : TC, TC[_[_]]] = new Not[TC[M]] {}
}

import Not._

type NotNumeric[A] = Not[Numeric[A]]
implicitly[NotNumeric[String]] // succeeds
implicitly[NotNumeric[Int]] // fails

和伪代码,我问的问题看起来像这样(实际代码):

// NotFunctor[M[_]] declared above
def notFunctor[M[_] : NotFunctor](m: M[_]) = s"$m is not a functor"

更新:类似的技术应用到隐式转换:

import scala.language.higherKinds

trait Not[A]

object Not {
  implicit def not[V[_], A](a: A) = new Not[V[A]] {}
  implicit def notNot[V[_], A <% V[A]](a: A) = new Not[V[A]] {}
}

现在,我们可以(例如)定义一个函数,如果是有序的类型是不可见的,将只承认值:

def unordered[A <% Not[Ordered[A]]](a: A) = a


文章来源: Using context bounds “negatively” to ensure type class instance is absent from scope