nad*_*vwr 16 scala implicit typeclass higher-kinded-types scalaz
tl; dr:我如何做类似下面的编写代码:
def notFunctor[M[_] : Not[Functor]](m: M[_]) = s"$m is not a functor"
Run Code Online (Sandbox Code Playgroud)
' 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))
}
Run Code Online (Sandbox Code Playgroud)
请记住,fixedSizeOf()
在相关的情况下这是更可取的,因为它可以节省我们对集合的遍历.
这样,对于仅Length
定义(但不定义Foldable
)的容器类型,以及FixedSizeOf
定义了a的元素,我们获得了改进的性能.
对于其他案例,我们会查看收集并总结各个尺寸.
我的问题是在容器中定义了两者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))
Run Code Online (Sandbox Code Playgroud)
我想要的是Foldable
只有在Length
+ FixedSizeOf
组合不适用时才能依赖类型类.
为此,我可以更改foldSizeOf()
接受VarSizeOf
元素的定义:
implicit def foldSizeOfVar[T[_] : Foldable, A : VarSizeOf] = // ...
Run Code Online (Sandbox Code Playgroud)
现在我们必须填写有问题的部分,它涵盖了Foldable
带FixedSizeOf
元素的容器,没有Length
定义.我不知道如何处理这个,但伪代码看起来像:
implicit def foldSizeOfFixed[T[_] : Foldable : Not[Length], A : FixedSizeOf] = // ...
Run Code Online (Sandbox Code Playgroud)
Not[Length]
显然,' '在这里是组成部分.
我知道的部分解决方案
1)为低优先级implicits定义一个类并对其进行扩展,如' object Predef extends LowPriorityImplicits
'中所示.最后一个implicit(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))
}
}
}
Run Code Online (Sandbox Code Playgroud)
除了这与fixedSizeOf()
之前的碰撞,这仍然是必要的.
感谢您的帮助或观点:-)
nad*_*vwr 14
我最终使用基于歧义的解决方案解决了这个问题,该解决方案不需要使用继承进行优先级排序.
这是我试图概括这一点.
我们使用类型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]]
Run Code Online (Sandbox Code Playgroud)
...然后可以用作上下文边界:
def foo[T: NotMonoid] = ...
Run Code Online (Sandbox Code Playgroud)
我们继续确保Not [A]的每个有效表达式都将获得至少一个隐式实例.
implicit def notA[A, TC[_]] = new Not[TC[A]] {}
Run Code Online (Sandbox Code Playgroud)
该实例被称为'notA' - 'not',因为如果它是'Not [TC [A]]'找到的唯一实例,则发现负类型类适用; 对于处理扁平形状(例如Int)的方法,通常附加'A'.
我们现在引入一个模糊性来消除应用了不需要的类型类的情况:
implicit def notNotA[A : TC, TC[_]] = new Not[TC[A]] {}
Run Code Online (Sandbox Code Playgroud)
这与'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
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好!但是,上述方案尚不支持形状为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
Run Code Online (Sandbox Code Playgroud)
很简单.请注意,Scalaz有一个针对样板的解决方法,因为它处理了几种类型的形状 - 寻找'Unapply'.我无法使用它作为基本情况(形状TC [_]的类型类,如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
Run Code Online (Sandbox Code Playgroud)
我在问题中要求的伪代码看起来像这样(实际代码):
// NotFunctor[M[_]] declared above
def notFunctor[M[_] : NotFunctor](m: M[_]) = s"$m is not a functor"
Run Code Online (Sandbox Code Playgroud)
更新:应用于隐式转换的类似技术:
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]] {}
}
Run Code Online (Sandbox Code Playgroud)
我们现在可以(例如)定义一个函数,只有当它们的类型不能被视为Ordered时才会允许值:
def unordered[A <% Not[Ordered[A]]](a: A) = a
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1146 次 |
最近记录: |