I'd like to have a base case class I can extend at will into different types. I had something like this working previously, but I must have screwed something up, because now I'm getting compile errors.
trait JobLike
case class Task(name: String) {
def as[T <: JobLike]: Task with T = new Task(this.name) with T
}
The problem is, this gives me a compiler error like this:
java.lang.UnsupportedOperationException: addChild inapplicable for type T
at scala.reflect.internal.Symbols$Symbol.addChild(Symbols.scala:1835)
at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$templateSig$1(Namers.scala:1119)
at scala.tools.nsc.typechecker.Namers$Namer.templateSig(Namers.scala:1107)
at scala.tools.nsc.typechecker.Namers$Namer.classSig(Namers.scala:1178)
at scala.tools.nsc.typechecker.Namers$Namer.memberSig(Namers.scala:1788)
at scala.tools.nsc.typechecker.Namers$Namer.typeSig(Namers.scala:1751)
at scala.tools.nsc.typechecker.Namers$Namer$MonoTypeCompleter.completeImpl(Namers.scala:836)
...
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:82)
at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:85)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:101)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)</code>
I swear, I had something working yesterday that basically did just this, but now it's throwing this error.
You managed to hit a compiler bug! While that's certainly an issue, and should be reported, the thing is that your code isn't possible anyway, and it's better that it's crashing and bringing you here than "working" and doing something horribly wrong. Because T can be any subtype of JobLike, there's simply no way to mix it in at compile time. I could very well do trait Boom extends JobLike { def something: Int }; task.as[Boom], and then task couldn't create a Boom because there's no implementation for something.
You can provide asJobLike: Task with JobLike instead of a general as[T]: Task with T, which is completely possible. (Note that it's discouraged to make subtypes of case classes, so you may want to create a new class. The reason is that it breaks the reflexivity of equals.)
Something rather filthy that you can do is call (new Task(this.name) with JobLike).asInstanceOf[Task with T]. asInstanceOf only checks the cast for the leftmost type in the with, so this will not ClassCastException off the bat (you can also do "a".asInstanceOf[String with Int].). It will generally sorta work (modulus asInstanceOf[T] being false) as long as T has no members.
Something horrible that you can also do is use runtime reflection to dynamically create the required class. The trick as a whole is filthy and completely unsafe, so I won't say it here, but have a link.
Related
Some context so that this question actually makes some sense.
I have a trait from which I extend a case class and an object.
I have a method that pattern matches to decide which to pull based on given conditions.
When I pattern match and get the case class, everything works fine. But when I pattern match and get the object, an error is thrown.
Within the trait, I have methods that call the DB. And I also have vals that need overriding.
The curious thing is the error that is thrown when it the object is instantiated, is a DB specific error
java.sql.SQLInvalidAuthorizationSpecException: (conn=41) Transaction characteristics can't be changed while a transaction is in progress
Which led me on wild goose chase to hunt down whether the code was opening up another session unknowingly etc. But having checked my code, I knew I was passing around the one DB connection I had started and there wasn't any strange behaviour.
I figured, since my case class instance works, I wanted to see if converting my object into a case class would change anything.
Having converted my object to a case class, I then got an NPE error thrown which pointed me directly to the errant line of code that was causing problems. I then changed my vals to def in the trait and everything works fine.
So my questions is: Why is it that when I converted the object to the case class, it then threw up the error that was the true culprit. Which is why I asked the question: how is a case class vs object instantiated? Because I suspect this was the cause of the misleading error being thrown.
Feel free to correct my assumption if it is wrong.
Here is the crucial line:
I then changed my vals to def in the trait and everything works fine.
The problem was almost certainly caused by an abstract val in the trait being used in the constructor before it had been initialised by the subclass.
As a general rule, abstract values in a trait should always be def.
I would like to be able to pass an optional reason for a "None". I tried just extending 'None' ex:
case class NoneReason(reason: String) extends None
but get a "not found: type None", then I tried:
case class NoneReason(reason: String) extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}
but I get a ""illegal inheritance from sealed class Option"
I'm guessing this is a special case because 'None' is actually an alias for null or something.
I considered copying the Option source and renaming it to TriOption or something, but this seems gross to maintain. What would be an elegant way to get around this?
An optional reason for None is an Either[Option[String], Foo], where Foo is your type. You can't extend None; it's a singleton, so you can consider it to be a value like null.
But the Either class is made to select between two alternatives, with the right branch by convention containing a "correct" answer (if one is more correct than the other). If you want an optional error message, that goes in the left branch. Thus, you can switch to the type shown above and then wherever you would normally use Option you can x.right.toOption to convert to an option without a message, or use pattern matching or whatever, e.g.
x match {
case Right(foo) => useFoo(foo)
case Left(None) => throw new Exception("Something went wrong.")
case Left(Some(msg)) => throw new Exception(msg + " went wrong.")
}
If you find this to have too much boilerplate, you could use ScalaUtils or Scalaz or any of a number of other libraries that have an Option-with-reason alternative. ScalaUtils is really easy to get up to speed with. Scalaz is much deeper, which if you need the depth is awesome and if you don't means that it takes longer to start being productive.
None is not alias to null. it is an object (singleton) which extends Option[Nothing].
The reason you cannot extend it is that Option is sealed class, which mean it can be extends only by classes in the same file as the sealed class.
The way to go is, as Lee wrote in the comment to your question, is to use Try[T] or Either[String, T].
Here some nice explanation of how to use them.
I'm facing a design issue that my poor Scala level cannot handle.
I have an abstract class, let's say:
abstract class AbstractJob {
def run: Long
implicit val someVal: String
}
Then, I have "platforms" on which jobs can be ran.
I want to declare subclasses containing real "jobs" (no need to know what it should actually do). Ideally, I want them to be declared this way:
class OneJob with Platform1 with Platform2 {
override def run = {
... some code returning a long result ...
}
override implicit val someVal = "foo"
}
Indeed, jobs can have multiple platforms on which they can be ran.
However, the run method on jobs must be launched by the platforms. Therefore I tried to use a self-type:
trait Platform1 { self: AbstractJob =>
def runOnPlatform = someFunctionRelatedToPlatform(run(someVal))
}
Unfortunately when calling someVal value (in any job extending AbstractJob), I get a null value. I went to the conclusion that self-type in traits are directly related to the defined class (and not the actual class, which is a subtype in my case)
I tried to define another type type Jobs :> AbstractJob for the self-type in the trait but that didn't work.
I have a limited number of backup solutions, but I want to use the "full power" of Scala and avoid developers to write a lot of redundant and "plumber" code (related to platforms and AbstractJob class in my example).
AbstractJob calls directly runOnPlatform with an "abstract" function to be implemented in concrete jobs. In this case my users will only write the business code they need but I'm quite sure I can do better using Scala concepts. I'm feeling that I'm just using Java (and generally OOP) concepts in Scala...
Let the users write a hell lot of redundant code... Obviously I'm avoiding this as much as possible!!
I hope I'm clear enough!
I have a function with the following signature:
myFunc[T <: AnyRef](arg: T)(implicit m: Manifest[T]) = ???
How can I invoke this function if I do not know the exact type of the argument at the compile time?
For example:
val obj: AnyRef = new Foo() // At compile time obj is defined as AnyRef,
val objClass = obj.getClass // At runtime I can figure out that it is actually Foo
// Now I would need to call `myFunc[Foo](obj.asInstanceOf[Foo])`,
// but how would I do it without putting [Foo] in the square braces?
I would want to write something logically similar to:
myFunc[objClass](obj.asInstanceOf[objClass])
Thank you!
UPDATE:
The question is invalid - As #DaoWen, #Jelmo and #itsbruce correctly pointed, the thing I was trying to do was a complete nonsense! I just overthought the problem severely.
THANK YOU guys! It's too bad I cannot accept all the answers as correct :)
So, the problem was caused by the following situation:
I am using Salat library to serialize the objects to/from BSON/JSON representation.
Salat has an Grater[T] class which is used for both serialization and deserialization.
The method call for deserialization from BSON looks this way:
val foo = grater[Foo].asObject(bson)
Here, the role of type parameter is clear. What I was trying to do then is to use the same Grater to serialize any entity from my domain model. So I wrote:
val json = grater[???].toCompactJSON(obj)
I immediately rushed for reflection and just didn't see an obvious solution lying on the surface. Which is:
grater[Entity].toCompactJSON(obj) // where Entity...
#Salat trait Entity // is a root of the domain model hierarchy
Sometimes things are much easier than we think they are! :)
It appears that while I was writing this answer the author of the question realized that he does not need to resolve Manifests at runtime. However, in my opinion it is perfectly legal problem which I resolved successfully when I was writing Yaml [de]serialization library, so I'm leaving the answer here.
It is possible to do what you want using ClassTags or even TypeTags. I don't know about Manifests because that API is deprecated and I haven't worked with it, but I believe that with manifests it will be easier since they weren't as sophisticated as new Scala reflection. FYI, Manifest's successor is TypeTag.
Suppose you have the following functions:
def useClasstag[T: ClassTag](obj: T) = ...
def useTypetag[T: TypeTag](obj: T) = ...
and you need to call then with obj: AnyRef as an argument while providing either ClassTag or TypeTag for obj.getClass class as the implicit parameter.
ClassTag is the easiest one. You can create ClassTag directly from Class[_] instance:
useClasstag(obj)(ClassTag(obj.getClass))
That's all.
TypeTags are harder. You need to use Scala reflection to obtain one from the object, and then you have to use some internals of Scala reflection.
import scala.reflect.runtime.universe._
import scala.reflect.api
import api.{Universe, TypeCreator}
// Obtain runtime mirror for the class' classloader
val rm = runtimeMirror(obj.getClass.getClassLoader)
// Obtain instance mirror for obj
val im = rm.reflect(obj)
// Get obj's symbol object
val sym = im.symbol
// Get symbol's type signature - that's what you really want!
val tpe = sym.typeSignature
// Now the black magic begins: we create TypeTag manually
// First, make so-called type creator for the type we have just obtained
val tc = new TypeCreator {
def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
if (m eq rm) tpe.asInstanceOf[U # Type]
else throw new IllegalArgumentException(s"Type tag defined in $rm cannot be migrated to other mirrors.")
}
// Next, create a TypeTag using runtime mirror and type creator
val tt = TypeTag[AnyRef](rm, tc)
// Call our method
useTypetag(obj)(tt)
As you can see, this machinery is rather complex. It means that you should use it only if you really need it, and, as others have said, the cases when you really need it are very rare.
This isn't going to work. Think about it this way: You're asking the compiler to create a class Manifest (at compile time!) for a class that isn't known until run time.
However, I have the feeling you're approaching the problem the wrong way. Is AnyRef really the most you know about the type of Foo at compile time? If that's the case, how can you do anything useful with it? (You won't be able to call any methods on it except the few that are defined for AnyRef.)
It's not clear what you are trying to achieve and a little more context could be helpful. Anyway, here's my 2 cents.
Using Manifest will not help you here because the type parameter needs to be known at compile time. What I propose is something along these lines:
def myFunc[T](arg: AnyRef, klass: Class[T]) = {
val obj: T = klass.cast(arg)
//do something with obj... but what?
}
And you could call it like this:
myFunc(obj, Foo.class)
Note that I don't see how you can do something useful inside myFunc. At compile time, you cannot call any method on a object of type T beside the methods available for AnyRef. And if you want to use reflection to manipulate the argument of myFunc, then there is no need to cast it to a specific type.
This is the wrong way to work with a type-safe OO language. If you need to do this, your design is wrong.
myFunc[T <: AnyRef](arg: T)(implicit m: Manifest[T]) = ???
This is, of course, useless, as you have probably discovered. What kind of meaningful function can you call on an object which might be anything? You can't make any direct reference to its properties or methods.
I would want to write something logically similar to:
myFunc[objClass](obj.asInstanceOf[objClass])
Why? This kind of thing is generally only necessary for very specialised cases. Are you writing a framework that will use dependency injection, for example? If you're not doing some highly technical extension of Scala's capabilities, this should not be necessary.
I bet you know something more about the class, since you say you don't know the exact type. One big part of the way class-based OO works is that if you want to do something to a general type of objects (including all its subtypes), you put that behaviour into a method belonging to the class. Let subclasses override it if they need to.
Frankly, the way to do what you are attempting is to invoke the function in a context where you know enough about the type.
Why is the "abstract" keyword for class definition optional in Scala, and how do I force the Scala compiler to tell me when my class is abstract?
Here an example that I wrote in Eclipse:
class Toto[T] {
def get(index: Int): T
}
object Toto {
def create[T]: Toto[T] = new Toto[T]
}
This seems to be a perfectly valid class definition in Scala, although it does NOT define the required get method, and is NOT prefixed with abstract. If you don't need the abstract keyword, then why does it exist? And if you want to be told that your class is actually abstract, how do you get the compiler to tell you?
This is not valid scala code, abstract is required, and instanciation forbidden. From the spec (5.2, p63):
The abstract modifier is used in class
definitions. It is redundant for
traits, and mandatory for all other
classes which have incomplete members.
Ab- stract classes cannot be
instantiated (ยง6.10) with a
constructor invocation unless
followed by mixins and/or a refinement
which override all incomplete members
of the class. Only abstract classes
and traits can have abstract term
members.
The code produces an error in the REPL : error: class Toto needs to be abstract, since method get is not defined
I get the proper behavior with the same message in Eclipse too. You should check whether you get the same error with and without eclipse. Whichever is true, I guess if you have exactly the code you posted without an error (does it run?), a bug repport will be warranted.
To answer my own question: In Eclipse, you can only tell if a class is correct if all other classes compile without errors! In other word, you can't trust anything Eclipse says about a class unless there are no errors in other classes.
So if you have errors in several classes, then there is no way of knowing which ones are the real errors, and neither if a class without errors is correct.
You just have to repeatedly loop on the errors, fixing any one that makes sense, and hoping the others errors that don't make sense are eventually going to just disappear.