Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I have reduced your problem to traits, and I am starting to understand why you are getting into troubles with casts and abstract types. </p> <p>What you are actually missing is ad-hoc polymorphism, which you obtain through the following: - Writing a method with generic signature relying on an implicit of the same generic to delegate the work to - Making the implicit available only for specific value of that generic parameter, which will turn into a "implicit not found" compile time error when you try to do something illegal.</p> <p>Let's now look to the problem in order. The first is that the signature of your method is wrong for two reasons:</p> <ul> <li><p>When replacing a site you want to create a new monomer of the new generic type, much as you do when you add to a collection an object which is a superclass of the existing generic type: you get a new collection whose type parameter is the superclass. You should yield this new Monomer as a result.</p></li> <li><p>You are not sure that the operation will yield a result (in case you can't really replace a state). In such a case the right type it's Option[T]</p> <pre><code>def replaceSiteWithIntersection[A &gt;: StateSiteType &lt;: ReactantStateSite] (thisSite: A, monomer: ReactantMonomer): Option[MonomerClass[A]] </code></pre></li> </ul> <p>If we now look digger in the type errors, we can see that the real type error comes from this method:</p> <pre><code> thisSite.createIntersection </code></pre> <p>The reason is simple: it's signature is not coherent with the rest of your types, because it accepts a ReactantSite but you want to call it passing as parameter one of your stateSites (which is of type Seq[StateSiteType] ) but you have no guarantee that </p> <pre><code>StateSiteType&lt;:&lt;ReactantSite </code></pre> <p>Now let's see how evidences can help you:</p> <pre><code>trait Intersector[T] { def apply(observed: Seq[String]): T } trait StateSite { def name: String } trait ReactantStateSite extends StateSite { def observed: Seq[String] def createIntersection[A](otherSite: ReactantStateSite)(implicit intersector: Intersector[A]): A = { val newStates = observed.intersect(otherSite.observed) intersector(newStates) } } import Monomers._ trait MonomerClass[+StateSiteType &lt;: StateSite] { val stateSites: Seq[StateSiteType] def replaceSiteWithIntersection[A &gt;: StateSiteType &lt;: ReactantStateSite](thisSite: A, otherMonomer: ReactantMonomer)(implicit intersector:Intersector[A], ev: StateSiteType &lt;:&lt; ReactantStateSite): Option[MonomerClass[A]] = { def replaceOrKeep(condition: (StateSiteType) =&gt; Boolean)(f: (StateSiteType) =&gt; A)(implicit ev: StateSiteType&lt;:&lt;A): Seq[A] = { stateSites.map { site =&gt; if (condition(site)) f(site) else site } } val reactantSiteToIntersect:Option[ReactantStateSite] = otherMonomer.stateSites.find(_.name == thisSite.name) reactantSiteToIntersect.map { siteToReplace =&gt; val newSites = replaceOrKeep {_ == thisSite } { item =&gt; thisSite.createIntersection( ev(item) ) } MonomerClass(newSites) } } } object MonomerClass { def apply[A &lt;: StateSite](sites:Seq[A]):MonomerClass[A] = new MonomerClass[A] { val stateSites = sites } } object Monomers{ type Monomer = MonomerClass[StateSite] type ReactantMonomer = MonomerClass[ReactantStateSite] type ProductMonomer = MonomerClass[ProductStateSite] type ProducedMonomer = MonomerClass[ProducedStateSite] } </code></pre> <ol> <li><p>Please note that this pattern can be used with no special imports if you use in a clever way implicit resolving rules (for example you put your insector in the companion object of Intersector trait, so that it will be automatically resolved).</p></li> <li><p>While this pattern works perfectly, there is a limitation connected to the fact that your solution works only for a specific StateSiteType. Scala collections solve a similar problem adding another implicit, which is call CanBuildFrom. In our case we will call it CanReact</p></li> </ol> <p>You will have to make your MonomerClass invariant, which might be a problem though (why do you need covariance, however?)</p> <pre><code>trait CanReact[A, B] { implicit val intersector: Intersector[B] def react(a: A, b: B): B def reactFunction(b:B) : A=&gt;B = react(_:A,b) } object CanReact { implicit def CanReactWithReactantSite[A&lt;:ReactantStateSite](implicit inters: Intersector[A]): CanReact[ReactantStateSite,A] = { new CanReact[ReactantStateSite,A] { val intersector = inters def react(a: ReactantStateSite, b: A) = a.createIntersection(b) } } } trait MonomerClass[StateSiteType &lt;: StateSite] { val stateSites: Seq[StateSiteType] def replaceSiteWithIntersection[A &gt;: StateSiteType &lt;: ReactantStateSite](thisSite: A, otherMonomer: ReactantMonomer)(implicit canReact:CanReact[StateSiteType,A]): Option[MonomerClass[A]] = { def replaceOrKeep(condition: (StateSiteType) =&gt; Boolean)(f: (StateSiteType) =&gt; A)(implicit ev: StateSiteType&lt;:&lt;A): Seq[A] = { stateSites.map { site =&gt; if (condition(site)) f(site) else site } } val reactantSiteToIntersect:Option[ReactantStateSite] = otherMonomer.stateSites.find(_.name == thisSite.name) reactantSiteToIntersect.map { siteToReplace =&gt; val newSites = replaceOrKeep {_ == thisSite } { canReact.reactFunction(thisSite)} MonomerClass(newSites) } } } </code></pre> <p>With such an implementation, whenever you want to make the possibility to replace a site with another site of a different type, all you need is to make available new implicit instances of CanReact with different types. </p> <p>I will conclude with a (I hope) clear explanation of why you should not need covariance. </p> <p>Let's say you have a <code>Consumer[T]</code> and a <code>Producer[T]</code>. </p> <p>You need covariance when you want to provide to the <code>Consumer[T1]</code> a <code>Producer[T2]</code> where <code>T2&lt;:&lt;T1</code> . But if you need to use the value produced by T2 inside T1, you can </p> <pre><code>class ConsumerOfStuff[T &lt;: CanBeContained] { def doWith(stuff: Stuff[T]) = stuff.t.writeSomething } trait CanBeContained { def writeSomething: Unit } class A extends CanBeContained { def writeSomething = println("hello") } class B extends A { override def writeSomething = println("goodbye") } class Stuff[T &lt;: CanBeContained](val t: T) object VarianceTest { val stuff1 = new Stuff(new A) val stuff2 = new Stuff(new B) val consumerOfStuff = new ConsumerOfStuff[A] consumerOfStuff.doWith(stuff2) } </code></pre> <p>This stuff clearly not compiles:</p> <blockquote> <p>error: type mismatch; found : Stuff[B] required: Stuff[A] Note: B &lt;: A, but class Stuff is invariant in type T. You may wish to define T as +T instead. (SLS 4.5) consumerOfStuff.doWith(stuff2).</p> </blockquote> <p>But again, this come from a misinterpretation of usage of variance, as <a href="https://stackoverflow.com/questions/5277526/real-world-examples-of-co-and-contravariance-in-scala">How are co- and contra-variance used in designing business applications?</a> Kris Nuttycombe answer explain. If we refactor like the following</p> <pre><code>class ConsumerOfStuff[T &lt;: CanBeContained] { def doWith[A&lt;:T](stuff: Stuff[A]) = stuff.t.writeSomething } </code></pre> <p>You could see everything compiling fine.</p>
    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. 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