I want to create a generic class that holds the value of an enumeration, and also allows access to the possible values of the enumeration. Think of a property editor for example - you need to know the current value of the property, and you also need to be able to know what other values are legal for the property. And the type of enumeration should not be known in advance, you should be able to work with any kind of enumeration.
My first thought was something like this:
class EnumerationProperty[T <: Enumeration](value:T)
However, that doesn't work because for enumerations T isn't a type, it's an object. Other variations I have tried are:
class EnumerationProperty[T <: Enumeration](value:T.Value)
class EnumerationProperty[T <: Enumeration.Value](value:T)
(I won't go into the details of why these don't work because I suspect the reasons aren't interesting.)
For first part of your question. You can define holder for generic enum value like this:
case class EnumerationProperty[T <: Enumeration#Value](value:T)
But I don't know how to get all enum values without explicitly pass Enumeration object. Enumeration has values() method to get all values. And Enumeration#Value has link to Enumeration, but with private access
In your use case (a property editor) I think the best solution is to rely on scala reflection available in scala 2.10. Given a class's TypeTag, you can get the full type (without erasure) of all of its members plus their values, and from that populate your property editor. For enumerations, use the TypeTag to get their values using the following method:
Using Scala 2.10 reflection how can I list the values of Enumeration?
Now, maybe you don't want or can't use scala reflection, and from now on I'll suppose this is the case. If that is true then you are opening a whole can of worm :)
TL;DR: This is not possible using a standard Enumeration, so you'll probably have to explcitly wrap the enumeration values as shown in Ptharien's Flame's answer (or roll your own fork of Enumeration). Below I detail all my attempts, please bear with me.
Unfortunately, and for some unknown reason to me (though I suspect it has to do with serialization issues), Enumeration.Value has no field pointing to its Enumeration instance. Given how Enumeration is implemented, this would be trivial to implement, but of course wehave no say, short of forking Enumeration and modifying our version (which is actually what I did for this very purpose, plus to add proper support for serialization and reflection - but I diggress).
If we can't modify Enumeration, maybe we can just extend it? Looking at the implementation again, something like this would seem to work:
class EnumerationEx extends Enumeration {
protected class ValEx(i: Int, name: String) extends Val(i, name) {
#transient val enum: Enumeration = EnumerationEx.this
}
override protected def Value(i: Int, name: String): Value = new ValEx(i, name)
}
object Colors extends EnumerationEx {
val Red, Green, Blue = Value
}
The downside would be that it only works for enumerations that explicitly extend EnumerationEx instead of Enumeration, but it would be better than nothing.
Unfortunately, this does not compile simply because def Value ... is declared final in Enumeration so there is no way to override it. (Note again that forking Enumeration would allow to circunvent this. Actually, why not do it, as we are already down the path of using a custom Enumeration anwyay. I'll let you judge).
So here is another take on it:
class EnumerationEx extends Enumeration {
class ValueWithEnum( inner: Value ) {
#transient val enum: Enumeration = EnumerationEx.this
}
implicit def valueToValue( value: Value ): ValueWithEnum = new ValueWithEnum( value )
}
And indeed it works as expected. Or so it seems.
scala> object Colors extends EnumerationEx {
| val Red, Green, Blue = Value
| }
defined module Colors
scala> val red = Colors.Red
red: Colors.Value = Red
scala> red.enum.values
res58: Enumeration#ValueSet = Colors.ValueSet(Red, Green, Blue)
Hooray? Well no, because the conversion from Value to ValueWithEnum is done only when accessing red.enum, not at the time of instantiation of the Colors enumeration. In other words, when calling enum the compiler needs to know the exact static type of the enumeration (the compiler must statically know that red's type is Colors.Value, and not just Enumeration# Value). And in the use case you mention (a property editor) you can only rely to java reflection (I already assumed that you won't use scala reflection) to get the type of an enumeration value, and java reflection will only give you Enumeration#Val (which extends Enumeration#Value) as the type of Colors.Red. So basically you are stuck here.
Your best bet is definitly to use scala reflection in the first place.
class EnumProps[E <: Enumeration](val e: E)(init: e.Value) {...}
Then you can use e and e.Value to implement the class.
Related
Sorry I'm not very familiar with Scala, but I'm curious if this is possible and haven't been able to figure out how.
Basically, I want to create some convenience initializers that can generate a random sample of data (in this case a grid). The grid will always be filled with instances of a particular type (in this case a Location). But in different cases I might want grids filled with different subtypes of Location, e.g. Farm or City.
In Python, this would be trivial:
def fillCollection(klass, size):
return [klass() for _ in range(size)]
class City: pass
cities = fillCollection(City, 10)
I tried to do something similar in Scala but it does not work:
def fillGrid[T <: Location](size): Vector[T] = {
Vector.fill[T](size, size) {
T()
}
}
The compiler just says "not found: value T"
So, it it possible to approximate the above Python code in Scala? If not, what's the recommended way to handle this kind of situation? I could write an initializer for each subtype, but in my real code there's a decent amount of boilerplate overlap between them so I'd like to share code if possible.
The best workaround I've come up with so far is to pass a closure into the initializer (which seems to be how the fill method on Vectors already works), e.g.:
def fillGrid[T <: Location](withElem: => T, size: Int = 100): Vector[T] = {
Vector.fill[T](n1 = size, n2 = size)(withElem)
}
That's not a huge inconvenience, but it makes me curious why Scala doesn't support the "simpler" Python-style construct (if it in fact doesn't). I sort of get why having a "fully generic" initializer could cause trouble, but in this case I can't see what the harm would be generically initializing instances that are all known to be subtypes of a given parent type.
You are correct, in that what you have is probably the simplest option. The reason Scala can't do things the pythonic way is because the type system is much stronger, and it has to contend with type erasure. Scala can not guarantee at compile time that any subclass of Location has a particular constructor, and it will only allow you to do things that it can guarantee will conform to the types (unless you do tricky things with reflection).
If you want to clean it up a little bit, you can make it work more like python by using implicits.
implicit def emptyFarm(): Farm = new Farm
implicit def emptyCity(): City = new City
def fillGrid[T <: Location](size: Int = 100)(implicit withElem: () => T): Vector[Vector[T]] = {
Vector.fill[T](n1 = size, n2 = size)(withElem())
}
fillGrid[farm](3)
To make this more usable in a library, it's common to put the implicits in a companion object of Location, so they can all be brought into scope where appropriate.
sealed trait Location
...
object Location
{
implicit def emptyFarm...
implicit def emptyCity...
}
...
import Location._
fillGrid[Farm](3)
You can use reflection to accomplish what you want...
This is a simple example that will only work if all your subclasses have a zero args constructor.
sealed trait Location
class Farm extends Location
class City extends Location
def fillGrid[T <: Location](size: Int)(implicit TTag: scala.reflect.ClassTag[T]): Vector[Vector[T]] = {
val TClass = TTag.runtimeClass
Vector.fill[T](size, size) { TClass.newInstance().asInstanceOf[T] }
}
However, I have never been a fan of runtime reflection, and I hope there could be another way.
Scala cannot do this kind of thing directly because it's not type safe. It will not work if you pass a class without a zero-argument constructor. The Python version throws an error at runtime if you try to do this.
The closure is probably the best way to go.
E.g.:
def updateAsinRecords(asins:Seq[String], recordType:String)
Above method takes a Seq of ASINs and a record type. Both have type of String. There are also other values that are passed around with type String in the application. Needless to say, this being Scala, I'd like to use the type system to my advantage. How to pass around string values in a type safe manner (like below)?
def updateAsinRecords(asins:Seq[ASIN], recordType:RecordType)
^ ^
I can imagine, having something like this:
trait ASIN { val value:String }
but I'm wondering if there's a better approach...
There is an excellent bit of new Scala functionality know as Value Classes and Universal Traits. They impose no runtime overhead but you can use them to work in a type safe manner:
class AnsiString(val inner: String) extends AnyVal
class Record(val inner: String) extends AnyVal
def updateAnsiRecords(ansi: Seq[AnsiString], record: Record)
They were created specifically for this purpose.
You could add thin wrappers with case classes:
case class ASIN(asin: String)
case class RecordType(recordType: String)
def updateAsinRecords(asins: Seq[ASIN], recordType: RecordType) = ???
updateAsinRecords(Vector(ASIN("a"), ASIN("b")), RecordType("c"))
This will not only make your code safer, but it will also make it much easier to read! The other big advantage of this approach is that refactoring later will be much easier. For example, if you decide later that an ASIN should have two fields instead of just one, then you just update the ASIN class definition instead of every place it's used. Likewise, you can do things like add methods to these types whenever you decide you need them.
In addition to the suggestions about using a Value Class / extends AnyVal, you should probably control the construction to allow only valid instances, since presumably not any old string is a valid ASIN. (And... is that an Amazon thing? It rings a bell somehow.)
The best way to do this is to make the constructor private and put a validating factory method in a companion object. The reason for this is that throwing exceptions in constructors (when an attempt is made to instantiate with an invalid argument) can lead to puzzling failure modes (I often see it manifest as a NoClassDefFoundError error when trying to load a different class).
So, in addition to:
case class ASIN private (asin: String) extends AnyVal { /* other stuff */ }
You should include something like this:
object A {
import scala.util.{Try, Success, Failure}
def fromString(str: String): Try[ASIN] =
if (validASIN(str))
Success(new ASIN(str))
else
Failure(new InvalidArgumentException(s"Invalid ASIN string: $str")
}
How about a type alias?
type ASIN = String
def update(asins: Seq[ASIN])
I have an interface defined using a structural type like this:
trait Foo {
def collection: {
def apply(a: Int) : String
def values() : collection.Iterable[String]
}
}
}
I wanted to have one of the implementers of this interface do so using a standard mutable HashMap:
class Bar {
val collection: HashMap[Int, String] = HashMap[Int, String]()
}
It compiles, but at runtime I get a NoSuchMethod exception when referring a Bar instance through a Foo typed variable. Dumping out the object's methods via reflection I see that the HashMap's apply method takes an Object due to type erasure, and there's some crazily renamed generated apply method that does take an int. Is there a way to make generics work with structural types? Note in this particular case I was able to solve my problem using an actual trait instead of a structural type and that is overall much cleaner.
Short answer is that the apply method parameter is causing you grief because it requires some implicit conversions of the parameter (Int => Integer). Implicits are resolved at compile time, the NoSuchMethodException is likely a result of these missing implicits.
Attempt to use the values method and it should work since there are no implicits being used.
I've attempted to find a way to make this example work but have had no success so far.
I'm reflectively invoking a method whose argument might or might not be an instance of a value class. As the purpose of value classes is to avoid boxing of underlying value, if the parameter type is value class then the method in question will in fact expect unboxed value. To handle this case I'm trying to unwrap the underlying value from value class. I first need to determine if the argument is of a value class, and here I hit the first stumbling block:
def isObjectOfValueClass(arg: Any) =
classOf[AnyVal].isAssignableFrom(arg.getClass)
This doesn't work as expected, as the method returns true for:
case class NonValueClass(underlying: Int)
How can isObjectOfValueClass be implemented? Or is there a simpler way to reflectively invoke a method that might take object of a value class as an argument?
First, note that your isObjectOfValueClass will get a boxed version of your value class instances.
Second, it cannot work like you want. It's because classOf[AnyVal] == classOf[AnyRef] == <java.lang.Object>.
There's no runtime way to distinguish between a boxed value class and a reference class (Any doesn't have .instanceOf[T], AnyVal cannot be used in pattern matching or as parameter of .instanceOf[T], and what's most important, compiled value classes do not extend or implement AnyVal).
If you want it decided on compile time, then try:
case class IsAnyVal[-T](val value: Boolean) extends AnyVal
implicit def _noClueHowToNameThisImplicit_1 = IsAnyVal[AnyVal](true)
implicit def _noClueHowToNameThisImplicit_2 = IsAnyVal[AnyRef](false)
def isAnyVal[T](arg: T)(implicit ev: IsAnyVal[T]) = ev.value
scala> isAnyVal(1)
res4: Boolean = true
scala> isAnyVal("")
res5: Boolean = false
I'm not sure how you want to extract the sole field of the detected boxed value class instances without more accidental boxing. Besides, Hotspot is pretty good at optimizing small short-lived objects.
Forgive me if the solution to this problem is too obvious or has been resolved already in this forum earlier (in which case, please point me to the post).
I have a class
org.personal.exercises.LengthContentsPair (l: Int, c: String)
{
val length = l
val contents = c
}
Then, in the same source file, I also define an implicit value which defines the way objects
of this type is to be ordered, thus:
object LengthContentsPair {
implicit val lengthContentsPairOrdering = new Ordering [LengthContentsPair] {
def compare (a: LengthContentsPair, b: LengthContentsPair)= {
a.length compare b.length;
}
}
}
following solutions given in this forum.
Now, I want to create a specialized Set which limits the number of elements in the Set to a given number. So, I define a separate class like this:
import scala.collection.immutable.TreeSet;
import org.personal.exercises.LengthContentsPair.lengthContentsPairOrdering;
class FixedSizedSortedSet [LengthContentsPair] extends TreeSet [LengthContentsPair]
{ ..
}
To me, this seems the correct way to subclass a TreeSet. But, the compiler throws the following error:
(1) No implicit Ordering defined for LengthContentsPair.
(2) not enough arguments for constructor TreeSet: (implicit ordering: Ordering[LengthContentsPair])scala.collection.immutable.TreeSet[LengthContentsPair]. Unspecified value parameter ordering.
Have I understood the scoping rules wrongly? It is something quite easy I feel, but I cannot put my hand on it.
You have defined FixedSizedSortedSet wrong. Your implementation has generic type parameter named LengthContentsPair which has nothing to do with your class with that name. In other words, you have shadowed LengthContentsPair class with generic type.
If you need a specialized set that only holds elements of LengthContentsPair, then you probably meant:
class FixedSizedSortedSet extends TreeSet[LengthContentsPair]
{ ..
}
This should work if an instance of Ordering[LengthContentsPair] is visible. But this shouldn't be a problem, since the ordering is defined in companion object of LengthContentsPair and is visible as implicit parameter by default.
But if you rather need a generic extension of TreeSet which can hold elements of any type, then you probably meant this:
class FixedSizedSortedSet[T](implicit ordering: Ordering[T]) extends TreeSet[T]
{ ..
}
Implicit parameter is needed because TreeSet requires an implicit Ordering[T], so we need to forward that requirement to FixedSizedSortedSet
BTW. I'd suggest you to consider replacing your LengthContentsPair class with a case class.