Verwenden Sie Kontextgrenzen "negativ", um sicherzustellen, dass keine Typklasseninstanz im Gültigkeitsbereich vorhanden ist
tl; dr: Wie mache ich so etwas wie den erfundenen Code unten:
def notFunctor[M[_] : Not[Functor]](m: M[_]) = s"$m is not a functor"
Das 'Not[Functor]
', hier der erfundene Teil zu sein.
Ich möchte, dass es gelingt, wenn das angegebene 'm' kein Functor ist und der Compiler ansonsten fehlschlägt.
Gelöst: Überspringen Sie den Rest der Frage und fahren Sie mit der Antwort unten fort.
Was ich versuche zu erreichen, ist grob gesagt "negativer Beweis".
Pseudocode würde ungefähr so aussehen:
// 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))
}
Denk daran, dassfixedSizeOf()
ist vorzuziehen, wo dies relevant ist, da es uns das Überqueren der Sammlung erspart.
Auf diese Weise nur für ContainertypenLength
ist definiert (aber nichtFoldable
) und für Elemente, bei denen aFixedSizeOf
definiert ist, erhalten wir eine verbesserte Leistung.
Für den Rest der Fälle gehen wir die Sammlung durch und summieren die einzelnen Größen.
Mein Problem ist in den Fällen, in denen beideLength
undFoldable
sind für den Container definiert undFixedSizeOf
ist für die Elemente definiert. Dies ist hier ein sehr häufiger Fall (z. B .:List[Int]
hat beides definiert).
Beispiel:
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))
Ich möchte mich auf das verlassen könnenFoldable
Typenklasse nur bei derLength
+FixedSizeOf
Kombination gilt nicht.
Zu diesem Zweck kann ich die Definition von ändernfoldSizeOf()
akzeptierenVarSizeOf
Elemente:
implicit def foldSizeOfVar[T[_] : Foldable, A : VarSizeOf] = // ...
Und jetzt müssen wir den problematischen Teil ausfüllen, der abdecktFoldable
Behälter mitFixedSizeOf
Elemente undNeinLength
definiert. Ich bin mir nicht sicher, wie ich das angehen soll, aber Pseudocode würde ungefähr so aussehen:
implicit def foldSizeOfFixed[T[_] : Foldable : Not[Length], A : FixedSizeOf] = // ...
Das 'Not[Length]
', offensichtlich, hier das erfundene Teil seiend.
Teillösungen, die mir bekannt sind
1) Definieren Sie eine Klasse für Implizite mit niedriger Priorität und erweitern Sie sie, wie in 'object Predef extends LowPriorityImplicits
'. Der letzte implizite (foldSizeOfFixed()
) kann in der übergeordneten Klasse definiert werden und wird alternativ von der abgeleiteten Klasse überschrieben.
Ich bin nicht an dieser Option interessiert, da ich möglicherweise die rekursive Verwendung von unterstützen möchteSizeOf
, und dies verhindert, dass sich das Implizite in der Basisklasse mit niedriger Priorität auf diejenigen in der Unterklasse verlässt (ist mein Verständnis hier korrekt? BEARBEITEN: falsch! Implizite Suche funktioniert aus dem Kontext der Unterklasse, dies ist eine praktikable Lösung!)
2) Ein gröberer Ansatz beruht aufOption[TypeClass]
(z.B.,:Option[Length[List]]
. Ein paar von denen und ich können nur ein großes, altes Implizit schreiben, das aufgreiftFoldable
undSizeOf
als obligatorisch undLength
undFixedSizeOf
als optional und stützt sich auf letztere, wenn sie verfügbar sind. (Quelle:Hier)
Die beiden Probleme sind mangelnde Modularität und das Zurückgreifen auf Laufzeitausnahmen, wenn keine relevanten Typklasseninstanzen gefunden werden können (dieses Beispiel kann möglicherweise für diese Lösung verwendet werden, aber das ist nicht immer möglich).
EDIT: Dies ist das Beste, was ich mit optionalen Implicits erreichen konnte. Es ist noch nicht da:
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))
}
}
}
Nur kollidiert dies mitfixedSizeOf()
von früher, was noch nötig ist.
Danke für jede Hilfe oder Perspektive :-)