加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 使用上下文边界“负”来确保范围中不存在类型类实例

发布时间:2020-12-16 09:10:54 所属栏目:安全 来源:网络整理
导读:tl; dr:我如何做如下代码: def notFunctor[M[_] : Not[Functor]](m: M[_]) = s"$m is not a functor" 这个“不是[Functor]”,是这里的组成部分. 当我提供的’m’不是Functor时,我希望它成功,否则编译器失败. 解决:跳过问题的其余部分,然后右键回到下面的
tl; dr:我如何做如下代码:

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

这个“不是[Functor]”,是这里的组成部分.
当我提供的’m’不是Functor时,我希望它成功,否则编译器失败.

解决:跳过问题的其余部分,然后右键回到下面的答案.

大致来说,我正在努力完成的是“负面证据”.

伪代码看起来像这样:

// 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()在相关的情况下是最好的,因为它可以让我们遍历集合.

这样,对于只定义长度(但不可折叠)的容器类型,以及定义了FixedSizeOf的元素,我们可以提高性能.

对于其余的情况,我们将收集和总结个别大小.

我的问题是在容器中定义了Length和Foldable的情况下,为这些元素定义了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,3))

只要长度FixedSizeOf组合不适用,我想要的就是依靠Foldable类型类.

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

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

现在我们必须填写有问题的部分,其中包含FixedSizeOf元素的可折叠容器,并且没有定义长度.我不知道如何处理这个,但伪代码看起来像:

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

“不[长]”显然是这里的组成部分.

我知道的部分解决方案

1)定义一个低优先级隐含类并扩展它,如’Predef extends LowPriorityImplicits’所示.
最后一个隐式(foldSizeOfFixed())可以在父类中定义,并且将被替代从后代类覆盖.

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

2)较粗糙的方法依赖于Option [TypeClass](例如:Options [Length [List]].其中一些可以写一个大的隐式,可以选择Foldable和SizeOf作为强制,Length和FixedSizeOf作为可选,如果可用,则依赖于后者(来源:here)

这里的两个问题是缺少模块化,并且在没有相关类型类实例可以找到时可以回到运行时异常(这个例子可能被用来解决这个问题,但并不总是这样)

编辑:这是最好的我可以得到与可选的含义.还没有:

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()相冲突,这仍然是必要的.

感谢任何帮助或观点:-)

解决方法

我最终使用基于歧义的解决方案来解决这个问题,而不需要使用继承进行优先级排序.

这是我推测这一点的尝试.

我们使用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] = ...

我们继续确保Not [A]的每个有效表达式将获得至少一个隐式实例.

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

该实例被称为’notA’ – ‘not’,因为如果它是唯一发现’Not [TC [A]]’的实例,则发现负类型类适用;通常为处理平面形状(例如Int)的方法附加“A”.

我们现在引入一个歧义来消除应用不需要的类型类的情况:

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

这与“NotA”几乎完全相同,除了这里,我们只对“TC”指定的类型类的实例存在于隐式范围内的类型感兴趣.该实例被命名为’notNotA’,因为只是通过查找隐含的匹配,它将创建一个含有’notA’的模糊,无法隐含搜索(这是我们的目标).

我们来看一个使用示例.我们将使用上面的“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 [_ [_]]的类型.让我们为他们添加一些暗示:

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对于通过处理几种类型形状而产生的样板的解决方法 – 寻找“Unapply”.对于基本情况(类型类型为[[]]的类型,如Monoid),我无法使用它,即使它像TC [_ [_]](例如Functor)一样工作,就像一个魅力,所以这个答案并不包括在内.

如果有人有兴趣,以下是单个片段所需的一切:

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]] {}
}

我们现在可以(例如)定义一个函数,只有当它们的类型不可视为Ordered时才会接受值:

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读