Użycie granic kontekstu „negatywnie” w celu zapewnienia, że ​​instancja klasy typu jest nieobecna w zakresie

tl; dr: Jak zrobić coś takiego, jak poniższy kod:

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

The 'Not[Functor]„, będąc tutaj częścią.
Chcę, żeby się powiodło, gdy dostarczone „m” nie jest Funktorem, a kompilator zawiedzie inaczej.

Rozwiązany: pomiń resztę pytania i przejdź do odpowiedzi poniżej.

To, co staram się osiągnąć, to z grubsza „negatywne dowody”.

Pseudo kod wyglądałby mniej więcej tak:

// 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))
}

Weź pod uwagę, żefixedSizeOf() jest preferowany tam, gdzie jest to istotne, ponieważ oszczędza nam przejścia przez kolekcję.

W ten sposób dla typów kontenerów, gdzie tylkoLength jest zdefiniowany (ale nieFoldable), a dla elementów, gdzieFixedSizeOf jest zdefiniowany, uzyskujemy lepszą wydajność.

W pozostałych przypadkach przeglądamy kolekcję i sumujemy poszczególne rozmiary.

Mój problem dotyczy przypadków, w których obaLength iFoldable są zdefiniowane dla kontenera iFixedSizeOf jest zdefiniowany dla elementów. Jest to bardzo częsty przypadek tutaj (np.:List[Int] ma oba zdefiniowane).

Przykład:

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

Chciałbym móc polegać naFoldable typ klasy tylko wtedy, gdyLength+FixedSizeOf połączenie nie ma zastosowania.

W tym celu mogę zmienić definicjęfoldSizeOf() akceptowaćVarSizeOf elementy:

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

A teraz musimy wypełnić problematyczną część, która obejmujeFoldable pojemniki zFixedSizeOf elementy iNieLength zdefiniowane. Nie jestem pewien, jak podejść do tego, ale pseudo kod wygląda tak:

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

The 'Not[Length]„oczywiście, będąc tutaj częścią.

Częściowe rozwiązania, o których wiem

1) Zdefiniuj klasę dla implikacji o niskim priorytecie i rozszerz ją, jak widać w 'object Predef extends LowPriorityImplicits” Ostatni ukryty (foldSizeOfFixed()) może być zdefiniowany w klasie nadrzędnej i zostanie zastąpiony przez alternatywę z klasy potomnej.

Nie interesuje mnie ta opcja, ponieważ chciałbym w końcu móc obsługiwać rekurencyjne użycieSizeOf, a to uniemożliwi domniemanie w klasie bazowej o niskim priorytecie polegania na tych w podklasie (czy moje zrozumienie tutaj jest poprawne? EDIT: błędne! niejawne wyszukiwanie działa z kontekstu podklasy, jest to realne rozwiązanie!)

2) Od tego zależy bardziej surowe podejścieOption[TypeClass] (na przykład.,:Option[Length[List]]. Kilka z nich i mogę po prostu napisać jeden wielki olbrzymi, który wybieraFoldable iSizeOf jako obowiązkowe iLength iFixedSizeOf jako opcjonalne i opiera się na tym ostatnim, jeśli są dostępne. (źródło:tutaj)

Dwa problemy w tym przypadku to brak modularności i powrót do wyjątków czasu wykonywania, gdy nie można zlokalizować odpowiednich instancji klasy typów (ten przykład można prawdopodobnie zastosować do tego rozwiązania, ale nie zawsze jest to możliwe)

EDYCJA: To jest najlepsze, jakie udało mi się uzyskać dzięki opcjonalnym implikacjom. Jeszcze go tam nie ma:

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

Z wyjątkiem tego, z którym się zderzafixedSizeOf() z wcześniejszej, która nadal jest konieczna.

Dziękuję za wszelką pomoc lub perspektywę :-)

questionAnswers(1)

yourAnswerToTheQuestion