2016-06-01 10 views
2

würde Ich mag einige Verhalten für diese Typen definieren, die keine Instanz für eine bestimmte typeclass hat:Wie passe ich Typen an, die keine bestimmte Typklasseninstanz haben?

// Given 
    trait SomeTypeclass[T] 

    // when we have implicit SomeTypeclass[T] 
    def f[T: SomeTypeclass](x:T):Unit = ??? 
    // when we don't have instance 
    def f[T !: SomeTypeclass](x: T):Unit = ??? 

Wir können den Unterschied in der typeclass handhaben, aber dann würde ich nur zusätzliche Instanzen erstellen muß, um unterstützt ein generisches Verhalten.

Gibt es eine Möglichkeit, einen gebundenen Typ zu negieren? Ein Weg, um die Funktion mit !: zu kompilieren?

(Ich möchte dies in Vanille Scala tun, ohne scalaz, formlos, etc)

+0

[Miles Sabins Typ Negation in der Praxis] (http://vpatryshev.blogspot.com/2012/03/miles-sabins-type-negation-in-practice.html) – muhuk

Antwort

2

Gibt es eine Möglichkeit, eine Art gebunden zu negieren?

Nein! Die Syntax [T: SomeTypeclass] ist nur Kurzform für (implicit val t: Sometypeclass[T]), und es gibt keine Möglichkeit, das zu "negieren". Sie könnten die Methode überladen, aber das würde eine Mehrdeutigkeit erzeugen.

Aber Sie können Klassen "verschachteln".

trait ExistsLowPri { 
    implicit def no[A]: Exists[A] = Exists.No 
} 
object Exists extends ExistsLowPri { 
    case class Yes[A](peer: A) extends Exists[A] 
    case object No extends Exists[Nothing] 

    implicit def yes[A](implicit peer: A): Exists[A] = new Yes(peer) 
} 
sealed trait Exists[+A] 

Beispiel:

trait Show[-A] { 
    def show(x: A): String 
} 

def test[A](x: A)(implicit ex: Exists[Show[A]]): Unit = println(
    ex match { 
    case Exists.Yes(s) => s.show(x) 
    case Exists.No  => "(no string repr)" 
    } 
) 

implicit object ShowBoolean extends Show[Boolean] { 
    def show(b: Boolean) = if (b) "T" else "F" 
} 

test(123) // (no string repr) 
test(true) // T 

Allerdings würde ich dringend abraten dies zu tun, weil der wichtigste Punkt der implicits und Typklassen ist explizit Compiler Ausfall zu haben, wenn etwas nicht in ihrem Umfang ist. Auf diese Weise können Sie immer kompilieren, haben aber keine Garantie, dass Sie eine bestimmte Typklasse korrekt in den Gültigkeitsbereich gebracht haben.

5

Sie können Ihre eigene Rolle:

trait NoInstance[T[_], A] 

object NoInstance extends LowPriorityNoInstance { 
    implicit def hasInstance0[T[_], A](implicit inst: T[A]): NoInstance[T, A] = ??? 
    implicit def hasInstance1[T[_], A](implicit inst: T[A]): NoInstance[T, A] = ??? 
} 

class LowPriorityNoInstance { 
    implicit def noInstance[T[_], A]: NoInstance[T, A] = new NoInstance[T, A] {} 
} 

Und dann:

scala> implicitly[NoInstance[Ordering, List[Int]]] 
res4: NoInstance[Ordering,List[Int]] = [email protected] 

scala> implicitly[NoInstance[Ordering, Int]] 
<console>:14: error: ambiguous implicit values: 
both method hasInstance0 in object NoInstance of type [T[_], A](implicit inst: T[A])NoInstance[T,A] 
and method hasInstance1 in object NoInstance of type [T[_], A](implicit inst: T[A])NoInstance[T,A] 
match expected type NoInstance[Ordering,Int] 
     implicitly[NoInstance[Ordering, Int]] 
       ^

In vielen Fällen können Sie diese Art der Sache mit dem Trick null Standard impliziten Parameter vermeiden können, aber:

def f[T](x: T)(implicit stc: SomeTypeclass[T] = null): Unit = Option(stc) match { 
    case Some(instance) => // do something in the case where we have an instance 
    case None => // do something in the case where we don't have an instance 
} 

Das fühlt sich an wie ein Hack, aber es funktioniert und die Leute benutzen es die ganze Zeit.

+0

Guter Punkt über den Standardwert '= null ', hatte nicht daran gedacht. –

+0

Das ist ein schöner Trick. Ich bin mir nicht sicher, ob ich den Standard "null" mag. – pedrofurla

+0

Das sind beide gute Antwort. Ich akzeptierte @ 0 __ 's, weil es mehr idiomatisch erscheint. 'Null'-Trick ist auch eine leichte Lösung, danke dafür. – muhuk

Verwandte Themen