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 :-)

Antworten auf die Frage(1)

Ihre Antwort auf die Frage