I have two classes, Holders (for lack of a better name at the moment) and Holder.
Holder has to be interfaced through Holders, which has an array of Holder of any type.
As such, it has to take Any type. I want to have the setValue do type checking that the Any input is indeed of type T.
I've read a bit about using manifests, but I'm getting somewhat lost. Is there any way to do what I want?
class Holders {
var values = Array[Any]()
var _holders = Array[Holder[_]]()
def setData(index: Int, newValue: Any) {
values(index) = newValue
_holders(index).setValue(newValue)
}
}
class Holder[T](someData: String, initValue: T) {
private var value : T = initValue
def getValue : T = value
def setValue(newValue: Any)(implicit m: Manifest[T]) = {
if (newValue.isInstanceOf[T])
value = newValue.asInstanceOf[T]
}
}
You can.
note: Manifest is deprecated and replaced with TypeTag and ClassTag, but that doesn't affect the remainder of this answer
Often when wanting Manifests/TypeTags you know exactly what's happening at compile time but want that information preserved through to runtime as well. In this case you also have to deal with the fact that, even at compile time, _holders(index) can't tell you what kind of Holder it's returning.
Depending on how _holders will be built, it may be possible to replace it with an Heterogeneous Map from the shapeless library, that would do exactly what you need out-of-the-box.
Otherwise, you have the right idea, testing the type at runtime. The trick is using TypeTag to capture both the underlying type of the holder and the type of the new value.
Note that the TypeTag context bound has to be specified on all the nested methods so it can be passed down the call stack in implicit scope. Presence of the TypeTag is what allows typeOf to then work.
import scala.reflect.runtime.universe._ //for TypeTag
class Holders {
var values = Array[Any]()
var _holders = Array[Holder[_]]()
def setData[V: TypeTag](index: Int, newValue: V): Unit = {
values(index) = newValue
_holders(index).setValue(newValue)
}
}
class Holder[T: TypeTag](someData: String, initValue: T) {
private var value: T = initValue
def getValue: T = value
def setValue[V: TypeTag](newValue: V): Unit =
if(typeOf[V] <:< typeOf[T]) {
value = newValue.asInstanceOf[T]
}
Or using Manifest
class Holder[T: Manifest](someData: String, initValue: T) {
private var value: T = initValue
def getValue: T = value
def setValue[V: Manifest](newValue: V): Unit =
if(manifest[V] <:< manifest[T]) {
value = newValue.asInstanceOf[T]
}
I'd strongly urge you to favour TypeTag though!
Type erasure makes this kind of thing... hard. In a nutshell once your code is compiled, all type parameters are replaced with Any. To understand the implications, consider the following example:
trait Foo[T] { def isT(a: Any): Boolean = a.isInstanceOf[T] }
object Bar extends Foo[String]
Bar.isT("foo") // true
Bar.isT(42) // also true, as Int <: Any
This will produce a warning when compiled with the appropriate options.
In this scenario you have two options; you can compare TypeTags, in which case you hope that the provided type parameters are sufficiently accurate (consider the provided type parameter could be any superclass of value), or you compare the runtime classes of your values (in which case you are out of luck when dealing with generic types). A TypeTag-based solution might look something like this:
class Holder[T : TypeTag](someData: String, initValue: T) {
private var value = initValue
def setValue[V : TypeTag](v: V): Unit = {
// Works because there are TypeTags for T and V in implicit scope
if(typeOf[V] <:< typeOf[T])
value = v.asInstanceOf[T]
}
}
Now you are looking at this and saying "well doesn't that mean that the assignment is actually value = v.asInstanceOf[Any]?" and the answer is yes - value is also erased to Any. Casting does nothing, in the sense that v.asInstanceOf[T] does not mean "convert v to a T". Instead what you are doing is saying "oh yeah, v is totally a T - honest!", and because the compiler is naive, it believes you.
Related
Lets say i have a class with chaining functions
class Vehicle {
protected var position: (Int, Int) = (0, 0)
def moveLeft(meters: Int): Vehicle = {
position = position._1 - meters -> position._2
this
}
def moveForward(meters: Int): Vehicle = {
position = position._1 -> (position._2 + meters)
this
}
}
so each method returns instance itself
Now i want to inherit Vehicle and add some method to new class
class Helicopter extends Vehicle {
protected var verticalDimension: Int = 0
def flyIntoTheSky(meters: Int): Helicopter = {
verticalDimension += meters
this
}
}
If i will create a new class instance and call any functions from parent, for example
new Helicopter().moveLeft(10).moveForward(20)
i will not be able to call new class method flyIntoTheSky because methods return type is Vehicle, and Vehicle does not know anything about methods of Helicopter.
2 obvious ways to solve it:
Either using asInstanceOf
new Helicopter()
.moveLeft(10)
.moveForward(20).asInstanceOf[Helocopter]
.flyIntoTheSky(10000)
either overriding each and avery parental method in new class
Both ways are not looking nice, i would prefer to leave all this type issues to Vehicle class and forget about it, so i found(as i hoped) the solution, rewriting methods like this
def moveLeft[T](meters: Int, vehicle: T = this): T = {
position = position._1 - meters -> position._2
this.asInstanceOf[T]
}
so i expected that return type will be taken from method's second parameter vehicle, which always equals to default value "this", and will return the value of current type. But unfortunately
new Helicopter().moveLeft(10)
still returns value of type Vehicle
res0: Vehicle = Helicopter#1de81c37
So the first question: why it doesn't work as i expected. And the second: is there are any beautiful way to solve this problem
Thank you
PS In google i found this solution in Java
https://www.andygibson.net/blog/article/implementing-chained-methods-in-subclasses/
, but i dont know java and can not translate it in scala
If the method always returns this you can give it return type this.type.
def moveLeft(meters: Int): this.type = {
position = position._1 - meters -> position._2
this
}
Addressing comment
strange, i cant get sense. If this.type refers to Helicopter why def
moveLeft[T](meters: Int, vehicle: T = this): T refers still to Vehicle
Note that this and this.type exist in two separate worlds, although this.type is "encroaching" on the value world as I will attempt to explain bellow. The former is a value whilst latter is a type, although a special kind of type. The type of expression this is not this.type, instead according to SLS:
The expression this... stands for the object being defined by the innermost
template or compound type enclosing the reference. If this is a
compound type, the type of this is that compound type. If it is a
template of a class or object definition with simple name đ¶, the type
of this is the same as the type of đ¶.this.
The innermost enclosing class of this in def moveLeft[T](meters: Int, vehicle: T = this): T is in fact Vehicle, therefore the static type of this is Vehicle.
Now consider type this.type:
this . type
| | |
value dot notation type
\ /
-------------------------------
|
value-dependent type
Notice the dot notation . which is used on a value, that is, if this was not a value then we would use # notation like so this#type. However it is indeed a value, which means type type depends on value this. Now, what value does it depend on? Well, no pun intended, it depends. Given the expression new Helicopter() it depends on the value (object) referred to by new Helicopter(), whilst given the expression new Car() it depends on the value referred to by expression new Car(). Important to understand here is that despite the fact that now this.type seems to be "changing" depending on a value, it is still a static type proper, meaning there is no runtime type-checking shenanigans.
I believe the source of confusion stems from the fact that we know, given new Helicopter(), in both cases, this refers to object of class Helicopter at runtime, however the key is to understand what static types can compiler determine, which in turn yields what members are invokable. In the case of this.type the compiler can determine the narrowest possible value-dependent singleton type uniquely inhabited by this very value.
As a side-note here is my attempt using shapeless lenses:
import shapeless._
case class Position(x: Double, y: Double, z: Double = 0)
sealed trait Vehicle
case class Car(p: Position) extends Vehicle
case class Plane(p: Position) extends Vehicle
object Vehicle {
implicit val carPosLens = lens[Car].p
implicit val planePosLens = lens[Plane].p
}
implicit class Move[T <: Vehicle](v: T) {
def moveX(d: Double)(implicit ev: Lens[T, Position]): T = ev.modify(v)(p => p.copy(x = p.x + d))
def moveY(d: Double)(implicit ev: Lens[T, Position]): T = ev.modify(v)(p => p.copy(y = p.y + d))
}
implicit class Fly[T <: Plane](v: T) {
def moveZ(d: Int)(implicit ev: Lens[T, Position]): T = ev.modify(v)(p => p.copy(z = p.z + d))
}
Plane(Position(0,0,0)).moveX(42).moveY(-3.142).moveZ(11)
Car(Position(0,0)).moveX(1)
which outputs
res0: Plane = Plane(Position(42.0,-3.142,11.0))
res1: Car = Car(Position(1.0,0.0,0.0))
I'm trying to implement something like clever parameters converter function with Scala.
Basically in my program I need to read parameters from a properties file, so obviously they are all strings and I would like then to convert each parameter in a specific type that I pass as parameter.
This is the implementation that I start coding:
def getParam[T](key : String , value : String, paramClass : T): Any = {
value match {
paramClass match {
case i if i == Int => value.trim.toInt
case b if b == Boolean => value.trim.toBoolean
case _ => value.trim
}
}
/* Exception handling is missing at the moment */
}
Usage:
val convertedInt = getParam("some.int.property.key", "10", Int)
val convertedBoolean = getParam("some.boolean.property.key", "true", Boolean)
val plainString = getParam("some.string.property.key", "value",String)
Points to note:
For my program now I need just 3 main type of type: String ,Int and Boolean,
if is possible I would like to extends to more object type
This is not clever, cause I need to explicit the matching against every possibile type to convert, I would like an more reflectional like approach
This code doesn't work, it give me compile error: "object java.lang.String is not a value" when I try to convert( actually no conversion happen because property values came as String).
Can anyone help me? I'm quite newbie in Scala and maybe I missing something
The Scala approach for a problem that you are trying to solve is context bounds. Given a type T you can require an object like ParamMeta[T], which will do all conversions for you. So you can rewrite your code to something like this:
trait ParamMeta[T] {
def apply(v: String): T
}
def getParam[T](key: String, value: String)(implicit meta: ParamMeta[T]): T =
meta(value.trim)
implicit case object IntMeta extends ParamMeta[Int] {
def apply(v: String): Int = v.toInt
}
// and so on
getParam[Int](/* ... */, "127") // = 127
There is even no need to throw exceptions! If you supply an unsupported type as getParam type argument, code will even not compile. You can rewrite signature of getParam using a syntax sugar for context bounds, T: Bound, which will require implicit value Bound[T], and you will need to use implicitly[Bound[T]] to access that values (because there will be no parameter name for it).
Also this code does not use reflection at all, because compiler searches for an implicit value ParamMeta[Int], founds it in object IntMeta and rewrites function call like getParam[Int](..., "127")(IntMeta), so it will get all required values at compile time.
If you feel that writing those case objects is too boilerplate, and you are sure that you will not need another method in these objects in future (for example, to convert T back to String), you can simplify declarations like this:
case class ParamMeta[T](f: String => T) {
def apply(s: String): T = f(s)
}
implicit val stringMeta = ParamMeta(identity)
implicit val intMeta = ParamMeta(_.toInt)
To avoid importing them every time you use getParam you can declare these implicits in a companion object of ParamMeta trait/case class, and Scala will pick them automatically.
As for original match approach, you can pass a implicit ClassTag[T] to your function, so you will be able to match classes. You do not need to create any values for ClassTag, as the compiler will pass it automatically. Here is a simple example how to do class matching:
import scala.reflect.ClassTag
import scala.reflect._
def test[T: ClassTag] = classTag[T].runtimeClass match {
case x if x == classOf[Int] => "I'm an int!"
case x if x == classOf[String] => "I'm a string!"
}
println(test[Int])
println(test[String])
However, this approach is less flexible than ParamMeta one, and ParamMeta should be preferred.
This issue is related to compiler behavior described in another issue
I think this is related to the way generics are implemented in compiler, and it has something to do with Inner Class compiler feature. I've a similar question and I know that this could be fixed with
if(m.compare(lowerBound.asInstanceOf[m.Measure]) >= 0) {
But I don't want to cast same type to same type all over my code. There should be good explanation why this happens. Should I fill in compiler bug somewhere ?
Without local variable m - error messages get even more weird.
trait MeasureBase {
type Measure <: MeasureBase
def compare(that: Measure): Int
def score: Double
}
case class DocTerm[Measure <: MeasureBase]
(val docID: Long, val measure:Measure)
extends Ordered[DocTerm[Measure]] {
def score = measure.score
def compare(that: DocTerm[Measure]): Int
= measure.compare(that.measure.asInstanceOf[measure.Measure])
}
class WriteCacheIterator[Measure <: MeasureBase]
(data:mutable.ArrayBuffer[DocTerm[Measure]]) {
var position = 0
def Next(lowerBound:Measure):Option[DocTerm[Measure]] = {
if(position < data.size - 1){
val m:Measure = data(position).measure
val b:Measure = lowerBound
if(m.compare(b) >= 0) {
//gives compiler error
//Error:(39, 20) type mismatch;
//found : b.type (with underlying type Measure)
//required: m.Measure
//if(m.compare(b) >= 0) {
}
} else return None
}
}
You have two different definitions for Measure:
the type parameter in DocTerm and other places, which is a subtype of MeasureBase.
a type member inside MeasureBase.
The two have nothing in common (except the name, but they are really different things). There is nothing linking your generic Measure inside WriteCacheIterator to the type member called Measure inside each and every element in your ArrayBuffer. You need to refactor the code to make this constraint explicit.
You could probably refactor the code to only use type parameters (generics) everywhere.
trait MeasureBase[Self <: MeasureBase[Self]] {...}
case class DocTerm[M <: MeasureBase[M](val docID: Long, val measure: M)
...
I just realized that my generic method:
def method[A](list: List[A]): A = { ... }
will result in a non-generic function type
val methodFun = method _
-> methodFun : (scala.List[Nothing]) => Nothing
when currying it, instead of keeping its generic type. Is there a possibility to keep the generic type information? I found out that I can define some explicit type as for example String by setting
val methodFun = method[String] _
-> methodFun : (scala.List[String]) => String
but this is not really what I want. I currently tend to use raw types to avoid this problems (as soon as I find out how) or is there a better solution?
Thanks for help!
PS: For why I want to do it:
def method1[A](list: List[A]): A = { ... }
def method2[A](element: A): Int = { ... }
// This will not cause a compiler error as stated before
// but this will result in (List[Nothing]) => Int
// but I want a (List[A]) => Int
val composedFun = method1 _ andThen method2
// The next line is possible
// but it gives me a (List[String]) => Int
val composedFunNonGeneric = method1[String] _ andThen method2[String]
Let's look at your example:
def method1[A](list: List[A]): A = { ... }
def method2[A](element: A): String = { ... }
// The next line will cause a compiler error
val composed = method1 _ andThen method2
First, that doesn't give me a compiler error, but rather has the too-specific type (List[Nothing]=>String) that you mentioned.
If you want to understand why this doesn't work, think about it this way: what is the type you're expecting for composed? I think you want something like this List[A]=>String. However, composed is a val, not a def (i.e. it's an instance of a function object, not a method). Object instances must have specific types. If you wanted to use a generic type here, then you'd have to wrap this val in a class definition with a generic type, but even then the generic type would be restricted to the type specified/inferred for each specific instance of that class.
In short, if you want to compose methods and keep the type parameter, you need to compose them manually and declare it with def instead:
def composed[A](list: List[A]): String = method2(method1(list))
I have posted several questions on SO recently dealing with Scala traits, representation types, member types, manifests, and implicit evidence. Behind these questions is my project to build modeling software for biological protein networks. Despite the immensely helpful answers, which have gotten me closer than I ever could get on my own, I have still not arrived at an solution for my project. A couple of answers have suggested that my design is flawed, which is why the solutions to the Foo-framed questions don't work in practice. Here I am posting a more complicated (but still greatly simplified) version of my problem. My hope is that the problem and solution will be broadly useful for people trying to build complex hierarchies of traits and classes in Scala.
The highest-level class in my project is the biological reaction rule. A rule describes how one or two reactants are transformed by a reaction. Each reactant is a graph that has nodes called monomers and edges that connect between named sites on the monomers. Each site also has a state that it can be in. Edit: The concept of the edges have been removed from the example code because they complicate the example without contributing much to the question. A rule might say something like this: there is one reactant made of monomer A bound to monomer B through sites a1 and b1, respectively; the bond is broken by the rule leaving sites a1 and b1 unbound; simultaneously on monomer A, the state of site a1 is changed from U to P. I would write this as:
A(a1~U-1).B(b1-1) -> A(a1~P) + B(b1)
(Parsing strings like this in Scala was so easy, it made my head spin.) The -1 indicates that bond #1 is between those sites--the number is just a arbitrary label.
Here is what I have so far along with the reasoning for why I added each component. It compiles, but only with gratuitous use of asInstanceOf. How do I get rid of the asInstanceOfs so that the types match?
I represent rules with a basic class:
case class Rule(
reactants: Seq[ReactantGraph], // The starting monomers and edges
producedMonomers: Seq[ProducedMonomer] // Only new monomers go here
) {
// Example method that shows different monomers being combined and down-cast
def combineIntoOneGraph: Graph = {
val all_monomers = reactants.flatMap(_.monomers) ++ producedMonomers
GraphClass(all_monomers)
}
}
The class for graphs GraphClass has type parameters because so that I can put constraints on what kinds of monomers and edges are allowed in a particular graph; for example, there cannot be any ProducedMonomers in the Reactant of a Rule. I would also like to be able to collect all the Monomers of a particular type, say ReactantMonomers. I use type aliases to manage the constraints.
case class GraphClass[
+MonomerType <: Monomer
](
monomers: Seq[MonomerType]
) {
// Methods that demonstrate the need for a manifest on MonomerClass
def justTheProductMonomers: Seq[ProductMonomer] = {
monomers.collect{
case x if isProductMonomer(x) => x.asInstanceOf[ProductMonomer]
}
}
def isProductMonomer(monomer: Monomer): Boolean = (
monomer.manifest <:< manifest[ProductStateSite]
)
}
// The most generic Graph
type Graph = GraphClass[Monomer]
// Anything allowed in a reactant
type ReactantGraph = GraphClass[ReactantMonomer]
// Anything allowed in a product, which I sometimes extract from a Rule
type ProductGraph = GraphClass[ProductMonomer]
The class for monomers MonomerClass has type parameters, as well, so that I can put constraints on the sites; for example, a ConsumedMonomer cannot have a StaticStateSite. Furthermore, I need to collect all the monomers of a particular type to, say, collect all the monomers in a rule that are in the product, so I add a Manifest to each type parameter.
case class MonomerClass[
+StateSiteType <: StateSite : Manifest
](
stateSites: Seq[StateSiteType]
) {
type MyType = MonomerClass[StateSiteType]
def manifest = implicitly[Manifest[_ <: StateSiteType]]
// Method that demonstrates the need for implicit evidence
// This is where it gets bad
def replaceSiteWithIntersection[A >: StateSiteType <: ReactantStateSite](
thisSite: A, // This is a member of this.stateSites
monomer: ReactantMonomer
)(
// Only the sites on ReactantMonomers have the Observed property
implicit evidence: MyType <:< ReactantMonomer
): MyType = {
val new_this = evidence(this) // implicit evidence usually needs some help
monomer.stateSites.find(_.name == thisSite.name) match {
case Some(otherSite) =>
val newSites = stateSites map {
case `thisSite` => (
thisSite.asInstanceOf[StateSiteType with ReactantStateSite]
.createIntersection(otherSite).asInstanceOf[StateSiteType]
)
case other => other
}
copy(stateSites = newSites)
case None => this
}
}
}
type Monomer = MonomerClass[StateSite]
type ReactantMonomer = MonomerClass[ReactantStateSite]
type ProductMonomer = MonomerClass[ProductStateSite]
type ConsumedMonomer = MonomerClass[ConsumedStateSite]
type ProducedMonomer = MonomerClass[ProducedStateSite]
type StaticMonomer = MonomerClass[StaticStateSite]
My current implementation for StateSite does not have type parameters; it is a standard hierarchy of traits, terminating in classes that have a name and some Strings that represent the appropriate state. (Be nice about using strings to hold object states; they are actually name classes in my real code.) One important purpose of these traits is provide functionality that all the subclasses need. Well, isn't that the purpose of all traits. My traits are special in that many of the methods make small changes to a property of the object that is common to all subclasses of the trait and then return a copy. It would be preferable if the return type matched the underlying type of the object. The lame way to do this is to make all the trait methods abstract, and copy the desired methods into all the subclasses. I am unsure of the proper Scala way to do this. Some sources suggest a member type MyType that stores the underlying type (shown here). Other sources suggest a representation type parameter.
trait StateSite {
type MyType <: StateSite
def name: String
}
trait ReactantStateSite extends StateSite {
type MyType <: ReactantStateSite
def observed: Seq[String]
def stateCopy(observed: Seq[String]): MyType
def createIntersection(otherSite: ReactantStateSite): MyType = {
val newStates = observed.intersect(otherSite.observed)
stateCopy(newStates)
}
}
trait ProductStateSite extends StateSite
trait ConservedStateSite extends ReactantStateSite with ProductStateSite
case class ConsumedStateSite(name: String, consumed: Seq[String])
extends ReactantStateSite {
type MyType = ConsumedStateSite
def observed = consumed
def stateCopy(observed: Seq[String]) = copy(consumed = observed)
}
case class ProducedStateSite(name: String, Produced: String)
extends ProductStateSite
case class ChangedStateSite(
name: String,
consumed: Seq[String],
Produced: String
)
extends ConservedStateSite {
type MyType = ChangedStateSite
def observed = consumed
def stateCopy(observed: Seq[String]) = copy(consumed = observed)
}
case class StaticStateSite(name: String, static: Seq[String])
extends ConservedStateSite {
type MyType = StaticStateSite
def observed = static
def stateCopy(observed: Seq[String]) = copy(static = observed)
}
My biggest problems are with methods framed like MonomerClass.replaceSiteWithIntersection. A lot of methods do some complicated search for particular members of the class, then pass those members to other functions where complicated changes are made to them and return a copy, which then replaces the original in a copy of the higher-level object. How should I parameterize methods (or the classes) so that the calls are type safe? Right now I can get the code to compile only with lots of asInstanceOfs everywhere. Scala is particularly unhappy with passing instances of a type or member parameter around because of two main reasons that I can see: (1) the covariant type parameter ends up as input to any method that takes them as input, and (2) it is difficult to convince Scala that a method that returns a copy indeed returns an object with exactly the same type as was put in.
I have undoubtedly left some things that will not be clear to everyone. If there are any details I need to add, or excess details I need to delete, I will try to be quick to clear things up.
Edit
#0__ replaced the replaceSiteWithIntersection with a method that compiled without asInstanceOf. Unfortunately, I can't find a way to call the method without a type error. His code is essentially the first method in this new class for MonomerClass; I added the second method that calls it.
case class MonomerClass[+StateSiteType <: StateSite/* : Manifest*/](
stateSites: Seq[StateSiteType]) {
type MyType = MonomerClass[StateSiteType]
//def manifest = implicitly[Manifest[_ <: StateSiteType]]
def replaceSiteWithIntersection[A <: ReactantStateSite { type MyType = A }]
(thisSite: A, otherMonomer: ReactantMonomer)
(implicit ev: this.type <:< MonomerClass[A])
: MonomerClass[A] = {
val new_this = ev(this)
otherMonomer.stateSites.find(_.name == thisSite.name) match {
case Some(otherSite) =>
val newSites = new_this.stateSites map {
case `thisSite` => thisSite.createIntersection(otherSite)
case other => other
}
copy(stateSites = newSites)
case None => new_this // This throws an exception in the real program
}
}
// Example method that calls the previous method
def replaceSomeSiteOnThisOtherMonomer(otherMonomer: ReactantMonomer)
(implicit ev: MyType <:< ReactantMonomer): MyType = {
// Find a state that is a current member of this.stateSites
// Obviously, a more sophisticated means of selection is actually used
val thisSite = ev(this).stateSites(0)
// I can't get this to compile even with asInstanceOf
replaceSiteWithIntersection(thisSite, otherMonomer)
}
}
I have reduced your problem to traits, and I am starting to understand why you are getting into troubles with casts and abstract types.
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.
Let's now look to the problem in order. The first is that the signature of your method is wrong for two reasons:
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.
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]
def replaceSiteWithIntersection[A >: StateSiteType <: ReactantStateSite]
(thisSite: A, monomer: ReactantMonomer): Option[MonomerClass[A]]
If we now look digger in the type errors, we can see that the real type error comes from this method:
thisSite.createIntersection
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
StateSiteType<:<ReactantSite
Now let's see how evidences can help you:
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 <: StateSite] {
val stateSites: Seq[StateSiteType]
def replaceSiteWithIntersection[A >: StateSiteType <: ReactantStateSite](thisSite: A, otherMonomer: ReactantMonomer)(implicit intersector:Intersector[A], ev: StateSiteType <:< ReactantStateSite): Option[MonomerClass[A]] = {
def replaceOrKeep(condition: (StateSiteType) => Boolean)(f: (StateSiteType) => A)(implicit ev: StateSiteType<:<A): Seq[A] = {
stateSites.map {
site => if (condition(site)) f(site) else site
}
}
val reactantSiteToIntersect:Option[ReactantStateSite] = otherMonomer.stateSites.find(_.name == thisSite.name)
reactantSiteToIntersect.map {
siteToReplace =>
val newSites = replaceOrKeep {_ == thisSite } { item => thisSite.createIntersection( ev(item) ) }
MonomerClass(newSites)
}
}
}
object MonomerClass {
def apply[A <: 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]
}
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).
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
You will have to make your MonomerClass invariant, which might be a problem though (why do you need covariance, however?)
trait CanReact[A, B] {
implicit val intersector: Intersector[B]
def react(a: A, b: B): B
def reactFunction(b:B) : A=>B = react(_:A,b)
}
object CanReact {
implicit def CanReactWithReactantSite[A<: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 <: StateSite] {
val stateSites: Seq[StateSiteType]
def replaceSiteWithIntersection[A >: StateSiteType <: ReactantStateSite](thisSite: A, otherMonomer: ReactantMonomer)(implicit canReact:CanReact[StateSiteType,A]): Option[MonomerClass[A]] = {
def replaceOrKeep(condition: (StateSiteType) => Boolean)(f: (StateSiteType) => A)(implicit ev: StateSiteType<:<A): Seq[A] = {
stateSites.map {
site => if (condition(site)) f(site) else site
}
}
val reactantSiteToIntersect:Option[ReactantStateSite] = otherMonomer.stateSites.find(_.name == thisSite.name)
reactantSiteToIntersect.map {
siteToReplace =>
val newSites = replaceOrKeep {_ == thisSite } { canReact.reactFunction(thisSite)}
MonomerClass(newSites)
}
}
}
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.
I will conclude with a (I hope) clear explanation of why you should not need covariance.
Let's say you have a Consumer[T] and a Producer[T].
You need covariance when you want to provide to the Consumer[T1] a Producer[T2] where T2<:<T1 . But if you need to use the value produced by T2 inside T1, you can
class ConsumerOfStuff[T <: 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 <: 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)
}
This stuff clearly not compiles:
error: type mismatch; found : Stuff[B] required: Stuff[A] Note: B <:
A, but class Stuff is invariant in type T. You may wish to define T as
+T instead. (SLS 4.5) consumerOfStuff.doWith(stuff2).
But again, this come from a misinterpretation of usage of variance, as How are co- and contra-variance used in designing business applications? Kris Nuttycombe answer explain. If we refactor like the following
class ConsumerOfStuff[T <: CanBeContained] {
def doWith[A<:T](stuff: Stuff[A]) = stuff.t.writeSomething
}
You could see everything compiling fine.
Not an answer, but what I can observe from looking over the question:
I see MonomerClass but not Monomer
My guts say you should avoid manifests when possible, as you have seen they can make things complicated. I don't think you will need them. For example the justTheProductMonomers method in GraphClass â since you have complete control over your class hierarchy, why not add test methods for anything involving runtime checks to Monomer directly? E.g.
trait Monomer {
def productOption: Option[ProductMonomer]
}
then you'll have
def justTheProductMonomers : Seq[ProductMonomer] = monomers.flatMap( _.productOption )
and so forth.
The problem here is that it seems you can have a generic monomer satisfying the product predicate, while you somehow want sub-type ProductMonomer.
The general advise I would give is first to define your matrix of tests that you need to process the rules, and then put those tests as methods into the particular traits, unless you have a flat hierarchy for which you can do pattern matching, which is easier since the disambiguation will appear concentrated at your use site, and not spread across all implementing types.
Also don't try to overdue it with compile-time type constraints. Often it's perfectly fine to have some constraints checked at runtime. That way at least you can construct a fully working system, and then you can try to spot the points where you can convert a runtime check into a compile time check, and decide whether the effort is worth it or not. It is appealing to solve things on the type level in Scala, because of its sophistication, but it also requires the most skills to do it right.
There are multiple problems. First, the whole method is weird: On the one hand you passing in a monomer argument, and if the argument thisState is found, the method has nothing to do with the receiverâthen why is this a method in MonomerClass at all and not a "free floating" functionâ, on the other hand you fall back to returning this if thisSite is not found. Since you originally had also implicit evidence: MyType <:< ReactantMonomer, my guess is the whole monomer argument is obsolete, and you actually wanted to operate on new_this.
A bit of cleanup, forgetting the manifests for the moment, you could have
case class MonomerClass[+StateSiteType <: StateSite, +EdgeSiteType <: EdgeSite](
stateSites: Seq[StateSiteType], edgeSites: Seq[EdgeSiteType]) {
def replaceSiteWithIntersection[A <: ReactantStateSite { type MyType = A }]
(thisSite: A)(implicit ev: this.type <:< MonomerClass[A, ReactantEdgeSite])
: MonomerClass[A, ReactantEdgeSite] = {
val monomer = ev(this)
monomer.stateSites.find(_.name == thisSite.name) match {
case Some(otherSite) =>
val newSites = monomer.stateSites map {
case `thisSite` => thisSite.createIntersection(otherSite)
case other => other
}
monomer.copy(stateSites = newSites)
case None => monomer
}
}
}
This was an interesting problem, it took me some iterations to get rid of the (wrong!) casting. Now it is actually quite readable: This method is restricted to the evidence that StateSiteType is actually a subtype A of ReactantStateSite. Therefore, the type parameter A <: ReactantStateSite { type MyType = A }âthe last bit is interesting, and this was a new find for myself: You can specify the type member here to make sure that your return type from createIntersection is actually A.
There is still something odd with your method, because if I'm not mistaken, you will end up calling x.createIntersection(x) (intersecting thisSite with itself, which is a no-op).
One thing that is flawed about replaceSiteWithIntersection is that according to the method signature the type of thisSite (A) is a super-type of StateSiteType and a sub-type of ReactantStateSite.
But then you eventually cast it to StateSiteType with ReactantStateSite. That doesn't make sense to me.
Where do you get the assurance from that A suddenly is a StateSiteType?