Ich versuche, Extension-Methoden zu einer bestehenden Klasse Elem
in Scala bereitzustellen. Ich möchte aber auch, dass die Operationen für alle M[Elem]
verfügbar sind, solange eine Scalaz Functor
für M
im Anwendungsbereich ist. Das Verhalten ist immer, die Operation auf den Funktor anzuwenden, indem map
verwendet.Scala implizit für beliebig tief Functor Zusammensetzung
import scalaz._
import Scalaz._
class Elem
implicit class Ops[F[_]: Functor, A <% Elem](self: F[A]) {
def foo = self.map(_ => "bar")
}
val elem = new Elem
// TODO (nice to have): can we avoid this "explicit implicit" conversion?
implicit def idOps[A <% Elem](self: A) = new Ops[Id, A](self)
elem.foo // bar
Option(elem).foo // Some(bar)
List(elem).foo // List(bar)
Ich möchte noch weiter gehen und meine Erweiterungsmethoden zur Verfügung stellen beliebig tief functors zu, wie List[Option[Elem]]
und Option[Option[Option[Elem]]]
. Ich war in der Lage eines impliziten schreiben Ops
für die Zusammensetzung von zwei functors Bereitstellung, aber ich war nicht in der Lage, es zu willkürlichen Verschachtelung Tiefen zu verallgemeinern:
// TODO: can we improve this to provide arbitrarily deep functor composition?
implicit def compositeOps[F[_]: Functor, G[_]: Functor, A <% Elem](self: F[G[A]]) = {
implicit val FG = implicitly[Functor[F]].compose[G]
new Ops[({ type FG[X] = F[G[X]] })#FG, A](self)
}
List(Option(elem)).foo // List(Some(bar))
Option(List(Option(elem))).foo // doesn't compile
Gibt es eine Möglichkeit, dies zu erreichen?
Danke für die Antwort! Ich habe zwei Probleme mit Ihrer Lösung gefunden: 1) Ich brauche wirklich eine implizite Umwandlung von 'A' in einen festen' Elem' Typ wie im Beispiel. Gibt es irgendeinen Weg, damit und mit deinem 'Helfer' zu arbeiten, außer dass das' A => Elem' als 'val' im Körper von' Helfer' enthalten ist?2) Der Compiler kann keine "Helfer" -Instanzen für Unterklassen der Funktoren selbst finden, z. Ich kann "Ops" nicht auf "Einige" anwenden. Gibt es eine Möglichkeit, diese Beschränkung aufzuheben? Mir ist bewusst, dass dies schon bei Scalaz-Klassen der Fall ist, aber in meinem Fall ist es besonders wichtig, dass ich das kann. –
1) '<%' ist nur Zucker für ein implizites 'A => Elem'; Ich habe das zu den CompositeOps im Beispiel hinzugefügt. 2) Es ist nicht wirklich möglich; Dies ist eine andauernde Debatte in scalaz, aber wenn wir Dinge als co/kontravariant deklarieren würden, würde dies zu Fällen führen, in denen "Any" abgeleitet wurde anstelle von was ein Typfehler sein sollte. Es ist möglich, einen "Functor [Some]" zu deklarieren, aber das wird Sie wahrscheinlich in Schwierigkeiten bringen. Der empfohlene Ansatz besteht darin, "intelligente Konstruktoren" wie scalazs "some" zu verwenden, die die entsprechenden Typen (in diesem Fall "Option [A]" und nicht "Some [A]") zurückgeben. – lmm
Ich wusste von '<%', aber ich wusste nicht, wie man den Typ von 'A1' erhält und dachte nicht an' {type A = A1} '. Und ich denke, ich werde dann das Problem mit den Unterklassen behandeln. Vielen Dank! –