Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I eventually solved this using an ambiguity-based solution that doesn't require prioritizing using inheritance.</p> <p>Here is my attempt at generalizing this.</p> <p>We use the type <code>Not[A]</code> to construct negative type classes:</p> <pre class="lang-scala prettyprint-override"><code>import scala.language.higherKinds trait Not[A] trait Monoid[_] // or import scalaz._, Scalaz._ type NotMonoid[A] = Not[Monoid[A]] trait Functor[_[_]] // or import scalaz._, Scalaz._ type NotFunctor[M[_]] = Not[Functor[M]] </code></pre> <p>...which can then be used as context bounds:</p> <pre class="lang-scala prettyprint-override"><code>def foo[T: NotMonoid] = ... </code></pre> <p>We proceed by ensuring that every valid expression of Not[A] will gain at least one implicit instance.</p> <pre class="lang-scala prettyprint-override"><code>implicit def notA[A, TC[_]] = new Not[TC[A]] {} </code></pre> <p>The instance is called 'notA' -- 'not' because if it is the only instance found for 'Not[TC[A]]' then the negative type class is found to apply; the 'A' is commonly appended for methods that deal with flat-shaped types (e.g. Int).</p> <p>We now introduce an ambiguity to turn away cases where the undesired type class <em>is</em> applied:</p> <pre class="lang-scala prettyprint-override"><code>implicit def notNotA[A : TC, TC[_]] = new Not[TC[A]] {} </code></pre> <p>This is almost exactly the same as 'NotA', except here we are only interested in types for which an instance of the type class specified by 'TC' exists in implicit scope. The instance is named 'notNotA', since by merely matching the implicit being looked up, it will create an ambiguity with 'notA', failing the implicit search (which is our goal).</p> <p>Let's go over a usage example. We'll use the 'NotMonoid' negative type class from above:</p> <pre class="lang-scala prettyprint-override"><code>implicitly[NotMonoid[java.io.File]] // succeeds implicitly[NotMonoid[Int]] // fails def showIfNotMonoid[A: NotMonoid](a: A) = a.toString showIfNotMonoid(3) // fails, good! showIfNotMonoid(scala.Console) // succeeds for anything that isn't a Monoid </code></pre> <p>So far so good! However, types shaped M[_] and type classes shaped TC[_[_]] aren't supported yet by the scheme above. Let's add implicits for them as well:</p> <pre class="lang-scala prettyprint-override"><code>implicit def notM[M[_], TC[_[_]]] = new Not[TC[M]] {} implicit def notNotM[M[_] : TC, TC[_[_]]] = new Not[TC[M]] {} implicitly[NotFunctor[List]] // fails implicitly[NotFunctor[Class]] // succeeds </code></pre> <p>Simple enough. Note that Scalaz has a workaround for the boilerplate resulting from dealing with several type shapes -- look for 'Unapply'. I haven't been able to make use of it for the basic case (type class of shape TC[_], such as Monoid), even though it worked on TC[_[_]] (e.g. Functor) like a charm, so this answer doesn't cover that.</p> <p>If anybody's interested, here's everything needed in a single snippet:</p> <pre class="lang-scala prettyprint-override"><code>import scala.language.higherKinds trait Not[A] object Not { implicit def notA[A, TC[_]] = new Not[TC[A]] {} implicit def notNotA[A : TC, TC[_]] = new Not[TC[A]] {} implicit def notM[M[_], TC[_[_]]] = new Not[TC[M]] {} implicit def notNotM[M[_] : TC, TC[_[_]]] = new Not[TC[M]] {} } import Not._ type NotNumeric[A] = Not[Numeric[A]] implicitly[NotNumeric[String]] // succeeds implicitly[NotNumeric[Int]] // fails </code></pre> <p>and the pseudo code I asked for in the question would look like so (actual code):</p> <pre class="lang-scala prettyprint-override"><code>// NotFunctor[M[_]] declared above def notFunctor[M[_] : NotFunctor](m: M[_]) = s"$m is not a functor" </code></pre> <p><strong>Update:</strong> Similar technique applied to implicit conversions:</p> <pre class="lang-scala prettyprint-override"><code>import scala.language.higherKinds trait Not[A] object Not { implicit def not[V[_], A](a: A) = new Not[V[A]] {} implicit def notNot[V[_], A &lt;% V[A]](a: A) = new Not[V[A]] {} } </code></pre> <p>We can now (e.g.) define a function that will only admit values if their types aren't viewable as Ordered:</p> <pre class="lang-scala prettyprint-override"><code>def unordered[A &lt;% Not[Ordered[A]]](a: A) = a </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

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