5 Stimmen

Die Erstellung eines spezifischeren Impliziten mithilfe eines strukturellen Typs in Scala

Meines Wissens gibt es kein gemeinsames Merkmal in der Sammlungsbibliothek, das die Methode map definiert (höchstwahrscheinlich, weil es unterschiedliche Signaturen für map gibt).

Ich habe einen beobachtbaren Wert (denken Sie an eine Eigenschaft in einem UI-System), der ein Änderungsereignis hat. Die beobachtbaren Werte können mit einer map-Methode gemappt werden.

Wenn wir jedoch mit einem Typ arbeiten, der bereits eine map-Methode hat, sollten wir die eingebaute Methode von map verwenden können.

Also statt:

prop map { x => 
  x map { actualX =>
   //etwas tun
  }
}

Möchte ich es so verwenden:

prop map { actualX =>
  //etwas tun
}

Ich habe einen vereinfachten Testfall. Zuerst die verschiedenen Teile, die ich benutze:

// den beobachtbaren Teil auslassen
trait ObservableValue[T] {
  def value: T
}

trait LowerPriorityImplicits {
  // Standardimplementierung, die eine reguläre map-Methode hinzufügt
  implicit class RichObservableValue1[A](o: ObservableValue[A]) {
    def map[B](f: A => B): ObservableValue[B] = new ObservableValue[B] {
      def value = f(o.value)
    }
  }
}

object ObservableValue extends LowerPriorityImplicits {

  // beschreibe einen Typ mit einer map-Methode
  type HasMapMethod[A, Container[X]] = {
    def map[B](f: A => B): Container[B]
  }

  // komplexe Implementierung, die die eingebaute map verwendet, wenn vorhanden
  implicit class RichObservableValue2[A, Container[Z] <: HasMapMethod[Z, Container]](
      o: ObservableValue[Container[A]]) {

    def map[B](f: A => B): ObservableValue[Container[B]] = 
      new ObservableValue[Container[B]] {
        def value = o.value.map(f)
      }
  }
}

Wenn in dem obigen Code etwas (oder vielleicht viel) falsch ist, lassen Sie es mich wissen. Ich möchte es so verwenden:

class TestCase extends ObservableValue[Option[Int]] {
  def value = None
}

val x = new TestCase

x map { value =>
  //dies schlägt fehl, weil der Compiler die Impliziten mit niedrigerer Priorität findet
  (value: Int).toString
}

// die Methode selbst funktioniert gut
ObservableValue.RichObservableValue2(x) map { value =>
  (value: Int).toString
}

Wenn ich Container[B] in Any ändere, wird die Implizite Konvertierung von RichObservableValue2 gefunden.

Mein Wissen darüber, wie Typen verwendet werden, um Implizite auszuwählen, ist begrenzt.

Ich habe versucht, die Antwort in den folgenden Quellen zu finden, aber das Thema ist etwas überwältigend:

Gibt es einen Weg, um diese Herausforderung zu lösen?

Bearbeiten

Ich kenne das FilterMonadic-Trait für Sammlungen. Ich suche nach einer Lösung, die die map-Methode erkennt, wie sie in der Klasse Option definiert ist.

Zweites Update

Es scheint, dass die Variante von FilterMonadic auch nicht funktioniert. Ich habe RichObservableValue3 zum Objekt RichObservableValue hinzugefügt.

implicit class RichObservableValue3[A, C[Z] <: FilterMonadic[Z, C[Z]]](o: ObservableValue[C[A]]) {

  def map[B, That](f: A => B)(implicit bf: CanBuildFrom[C[A], B, That]): ObservableValue[That] = new ObservableValue[That] {
    def value = o.value.map(f)

    val change = o.change map (_ map f)
  }
}

Und auch in diesem Fall wird diese Implizite Konvertierung nicht gewählt, obwohl ein List[Int] ein gültiges Argument ist. Ich scheine eine Regel zu verpassen, die verwendet wird, wenn Implizite ausgewählt werden.

0voto

EECOLOR Punkte 11104

Ich habe endlich den Teil gefunden, den ich vermisst habe. Um das Implizite abzugleichen musste ich den folgenden Typparameter hinzufügen:

T[Dummy <: Container[_]] <: ObservableValue[Dummy]

Das resultierende Objekt:

object ObservableValue {

  implicit class Mappable[A](o: ObservableValue[A]) {

    def map[B](f: A => B): ObservableValue[B] = new ObservableValue[B] {
      def value = f(o.value)
    }
  }

  // FMC = FilterMonadicContainer
  // D = Dummy
  implicit class FilterMonadicMappable[A, FMC[D] <: FilterMonadic[D, FMC[D]]](o: ObservableValue[FMC[A]]) {

    def map[B, That](f: A => B)(implicit bf: CanBuildFrom[FMC[A], B, That]): ObservableValue[That] = new ObservableValue[That] {
      def value = o.value.map(f)
    }
  }

  type HasMap[A, That[_]] = {
    def map[B](f: A => B): That[B]
  }

  // OVC = ObservableValueContainer
  // MC = MappableContainer
  // D = Dummy
  implicit class SimpleMappable[A, MC[D] <: HasMap[D, MC], OVC[D <: MC[_]] <: ObservableValue[D]](o: OVC[MC[A]]) {

    def map[B](f: A => B): ObservableValue[MC[B]] = new ObservableValue[MC[B]] {
      def value = o.value.map(f)
    }
  }

}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X