tl; dr: come faccio a fare qualcosa di simile al codice formato da qui di seguito:Utilizzando contesto delimita "negativo" per garantire istanza del tipo di classe è assente dal campo di applicazione
def notFunctor[M[_] : Not[Functor]](m: M[_]) = s"$m is not a functor"
Il 'Not[Functor]
', essendo la parte inventata qui.
Voglio che abbia successo quando la 'm' fornita non è un Functor e altrimenti fallisce il compilatore.
Risolto: salta il resto della domanda e vai avanti alla risposta sotto.
Quello che sto cercando di realizzare è, grosso modo, "prova negativa".
pseudo codice sarebbe simile modo:
// 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 => sizeOf(a))
}
Tenete a mente che fixedSizeOf()
è preferibile se del caso, dal momento che ci salva l'attraversamento sulla raccolta.
In questo modo, per i tipi di container in cui è definito solo Length
(ma non Foldable
) e per gli elementi in cui è definito un FixedSizeOf
, si ottiene un miglioramento delle prestazioni.
Per il resto dei casi, esaminiamo la raccolta e sommiamo le singole dimensioni.
Il mio problema si verifica nei casi in cui sia Length
sia Foldable
sono definiti per il contenitore e FixedSizeOf
è definito per gli elementi. Questo è un caso molto comune qui (ad es., List[Int]
ha entrambi definito).
Esempio:
scala> implicitly[SizeOf[List[Int]]].sizeOf(List(1,2,3))
<console>: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))
Quello che vorrei è quello di poter contare sulla classe Foldable
tipo solo quando la combinazione Length
+ FixedSizeOf
non si applica.
A tal fine, posso cambiare la definizione di foldSizeOf()
accettare VarSizeOf
elementi:
implicit def foldSizeOfVar[T[_] : Foldable, A : VarSizeOf] = // ...
E ora dobbiamo compilare la parte problematica che copre Foldable
contenitori con FixedSizeOf
elementi e non Length
definiti . Non sono sicuro di come affrontare questo, ma pseudo-codice sarebbe simile:
implicit def foldSizeOfFixed[T[_] : Foldable : Not[Length], A : FixedSizeOf] = // ...
Il 'Not[Length]
', ovviamente, essendo la parte costituita da qui.
parziali Sono a conoscenza di
1) Definire una classe per impliciti bassa priorità ed estenderlo, come si è visto in 'object Predef extends LowPriorityImplicits
'. L'ultimo implicito (foldSizeOfFixed()
) può essere definito nella classe genitore e verrà sostituito da un'alternativa dalla classe discendente.
Non sono interessato a questa opzione perché mi piacerebbe eventualmente essere in grado di supportare l'uso ricorsivo di SizeOf
, e ciò impedirà l'implicito nella classe di base a bassa priorità di fare affidamento su quelli della sottoclasse (è il mio comprensione qui corretto EDIT:?! sbagliato ricerca implicita lavora dal contesto della classe sub, questa è una soluzione praticabile)
2) un approccio più ruvida è basandosi su Option[TypeClass]
(ad esempio ,: Option[Length[List]]
alcuni di quelli e!. Posso solo scrivere un grande implicito che seleziona Foldable
e SizeOf
come obbligatorio e Length
e FixedSizeOf
come facoltativo, e si basa su quest'ultimo se sono disponibili. (Fonte: here)
I due problemi qui sono la mancanza di modularità e ricadere a runtime eccezioni quando non istanze di classe di tipo rilevanti possono essere posizionati (questo esempio può probabilmente essere fatto per lavorare con questa soluzione, ma questo non è sempre possibile)
EDIT: Questo è il meglio che sono riuscito a ottenere con impliciti facoltativi. Non è ancora arrivati:
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 <- implicitly[OptionalLength[T]]
sizeOf <- 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 => sizeOf.sizeOf(a))
}
}
}
Tranne questo si scontra con fixedSizeOf()
da prima, che è ancora necessario.
Grazie per qualsiasi aiuto o prospettiva :-)
Opzione sopra # 1 (quello con priorità implicite) funzionerà. Sono ancora interessato a qualcosa di più elegante, quindi la domanda rimane aperta per ora. – nadavwr
Forse puoi elaborare [il trucco di negazione (miglia di risposta, aggiornamento)] (http://stackoverflow.com/questions/6909053/enforce-type-difference/6944070#6944070) –
Il trucco di negazione potrebbe probabilmente essere fatto funzionare qui ma ritengo che la prioritizzazione sia di gran lunga la soluzione più desiderabile in questa situazione. –