简短概述: 我如何实现下面这个虚构代码的功能:
def notFunctor[M[_] : Not[Functor]](m: M[_]) = s"$m is not a functor"
这里的'Not[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()
是更可取的,因为它节省了我们对集合的遍历。这样,在仅定义了
Length
(但未定义Foldable
)的容器类型和定义了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,2,3))
我希望能够在
Length
+FixedSizeOf
组合不适用时仅依赖于Foldable
类型类。为此,我可以更改
foldSizeOf()
的定义以接受VarSizeOf
元素。implicit def foldSizeOfVar[T[_] : Foldable, A : VarSizeOf] = // ...
现在我们需要填写覆盖具有固定大小元素的可折叠容器和未定义长度的问题部分。我不确定如何处理这个问题,但伪代码可能如下:
implicit def foldSizeOfFixed[T[_] : Foldable : Not[Length], A : FixedSizeOf] = // ...
"
Not[Length]
"是显然虚构的部分。我知道的部分解决方法:
1)定义一个用于低优先级隐式的类并扩展它,就像在“
object Predef extends LowPriorityImplicits
”中看到的那样。最后一个隐式(foldSizeOfFixed()
)可以在父类中定义,并将被子类中的替代方案覆盖。我不感兴趣这个选项,因为我希望最终能够支持
SizeOf
的递归使用,而这将阻止低优先级基类中的隐式依赖于子类中的隐式(我的理解是否正确?编辑:不对!隐式查找从子类的上下文中工作,这是一个可行的解决方案!)2)一种更粗暴的方法是依赖于
Option[TypeClass]
(例如:Option[Length[List]]
)。有几个这样的实例,我只需编写一个大的隐式,将Foldable
和SizeOf
作为强制性参数选择,将Length
和FixedSizeOf
作为可选参数选择,如果它们可用则依赖于后者。(来源:这里)这里的两个问题是缺乏模块化和当找不到相关的类型类实例时回退到运行时异常(这个例子可能可以用这个解决方案,但这并不总是可能)。
编辑:这是我能够通过可选隐式获得的最佳结果。还不够好。
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()
相冲突,后者仍然是必要的。
感谢任何帮助或观点 :-)