Note that there are some explanatory texts on larger screens.

plurals
  1. POUsing context bounds "negatively" to ensure type class instance is absent from scope
    text
    copied! <p><strong>tl;dr</strong>: How do I do something like the made up code below:</p> <pre class="lang-scala prettyprint-override"><code>def notFunctor[M[_] : Not[Functor]](m: M[_]) = s"$m is not a functor" </code></pre> <p>The '<code>Not[Functor]</code>', being the made up part here.<br> I want it to succeed when the 'm' provided is not a Functor, and fail the compiler otherwise.</p> <p><strong>Solved</strong>: skip the rest of the question and go right ahead to the answer below.</p> <hr> <p>What I'm trying to accomplish is, roughly speaking, "negative evidence".</p> <p>Pseudo code would look something like so:</p> <pre class="lang-scala prettyprint-override"><code>// 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 =&gt; sizeOf(a)) } </code></pre> <p>Keep in mind that <code>fixedSizeOf()</code> is preferable where relevant, since it saves us the traversal over the collection.</p> <p>This way, for container types where only <code>Length</code> is defined (but not <code>Foldable</code>), and for elements where a <code>FixedSizeOf</code> is defined, we get improved performance.</p> <p>For the rest of the cases, we go over the collection and sum individual sizes.</p> <p>My problem is in the cases where both <code>Length</code> and <code>Foldable</code> are defined for the container, and <code>FixedSizeOf</code> is defined for the elements. This is a very common case here (e.g.,: <code>List[Int]</code> has both defined).</p> <p>Example:</p> <pre class="lang-scala prettyprint-override"><code>scala&gt; implicitly[SizeOf[List[Int]]].sizeOf(List(1,2,3)) &lt;console&gt;: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)) </code></pre> <p>What I would like is to be able to rely on the <code>Foldable</code> type class only when the <code>Length</code>+<code>FixedSizeOf</code> combination does not apply.</p> <p>For that purpose, I can change the definition of <code>foldSizeOf()</code> to accept <code>VarSizeOf</code> elements:</p> <pre class="lang-scala prettyprint-override"><code>implicit def foldSizeOfVar[T[_] : Foldable, A : VarSizeOf] = // ... </code></pre> <p>And now we have to fill in the problematic part that covers <code>Foldable</code> containers with <code>FixedSizeOf</code> elements and <em>no <code>Length</code> defined</em>. I'm not sure how to approach this, but pseudo-code would look something like:</p> <pre class="lang-scala prettyprint-override"><code>implicit def foldSizeOfFixed[T[_] : Foldable : Not[Length], A : FixedSizeOf] = // ... </code></pre> <p>The '<code>Not[Length]</code>', obviously, being the made up part here.</p> <p><strong>Partial solutions I am aware of</strong></p> <p>1) Define a class for low priority implicits and extend it, as seen in '<code>object Predef extends LowPriorityImplicits</code>'. The last implicit (<code>foldSizeOfFixed()</code>) can be defined in the parent class, and will be overridden by alternative from the descendant class.</p> <p>I am not interested in this option because I'd like to eventually be able to support recursive usage of <code>SizeOf</code>, and this will prevent the implicit in the low priority base class from relying on those in the sub class (is my understanding here correct? EDIT: wrong! implicit lookup works from the context of the sub class, this is a viable solution!)</p> <p>2) A rougher approach is relying on <code>Option[TypeClass]</code> (e.g.,: <code>Option[Length[List]]</code>. A few of those and I can just write one big ol' implicit that picks <code>Foldable</code> and <code>SizeOf</code> as mandatory and <code>Length</code> and <code>FixedSizeOf</code> as optional, and relies on the latter if they are available. (source: <a href="http://www.scala-lang.org/node/8773" rel="noreferrer">here</a>)</p> <p>The two problems here are lack of modularity and falling back to runtime exceptions when no relevant type class instances can be located (this example can probably be made to work with this solution, but that's not always possible)</p> <p>EDIT: This is the best I was able to get with optional implicits. It's not there yet:</p> <pre class="lang-scala prettyprint-override"><code>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 &lt;- implicitly[OptionalLength[T]] sizeOf &lt;- 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 =&gt; sizeOf.sizeOf(a)) } } } </code></pre> <p>Except this collides with <code>fixedSizeOf()</code> from earlier, which is still necessary.</p> <p>Thanks for any help or perspective :-)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload