tl; dr: как мне сделать что-то вроде составленного кода ниже:
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
), а для элементов, где a 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))
Я хотел бы иметь возможность полагаться на класс типа Foldable
только тогда, когда комбинация Length
+ FixedSizeOf
не применяется.
С этой целью я могу изменить определение foldSizeOf()
для принятия элементов VarSizeOf
:
implicit def foldSizeOfVar[T[_] : Foldable, A : VarSizeOf] = // ...
И теперь мы должны заполнить проблематичную часть, которая охватывает контейнеры Foldable
с элементами FixedSizeOf
и no Length
. Я не уверен, как подойти к этому, но псевдокод будет выглядеть примерно так:
implicit def foldSizeOfFixed[T[_] : Foldable : Not[Length], A : FixedSizeOf] = // ...
"Not[Length]
", очевидно, является составной частью здесь.
Частичные решения, о которых я знаю
1) Определите класс для невысоких приоритетов и продолжите его, как показано в 'object Predef extends LowPriorityImplicits
'.
Последний неявный (foldSizeOfFixed()
) может быть определен в родительском классе и будет переопределен альтернативой из класса потомков.
Мне неинтересен этот параметр, потому что я хотел бы в конечном итоге поддерживать рекурсивное использование SizeOf
, и это предотвратит использование неявного базового класса с низким приоритетом в подклассном классе (это мое понимание здесь правильно? EDIT: неправильный! неявный поиск работает из контекста подкласса, это жизнеспособное решение!)
2) Более грубый подход основан на Option[TypeClass]
(например,: Option[Length[List]]
. Некоторые из них и я могу просто написать один большой неявный символ, который выбирает Foldable
и SizeOf
как обязательные, и Length
и FixedSizeOf
как необязательные, и полагаются на последние, если они доступны. (источник: здесь)
Две проблемы здесь - отсутствие модульности и возврат к исключениям времени выполнения, когда не могут быть найдены соответствующие экземпляры класса типа (этот пример, вероятно, может быть сделан для работы с этим решением, но это не всегда возможно)
EDIT: Это лучшее, что я смог получить с дополнительными implicits. Это еще не все:
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()
, что по-прежнему необходимо.
Спасибо за любую помощь или перспективу: -)