I have this scala3 code:
val times4 = List.fill(4)
val oranges = times4("orange")
println(oranges)
Output is:
List(orange, orange, orange, orange)
But, is this correct behaviour? Because intellij tells me times4 is:
Nothing => List[Nothing]
But it is able to accept String type, while String is not a Nothing
Related
See this implementation follows the upper bound example http://docs.scala-lang.org/tutorials/tour/upper-type-bounds.html
class Fruit(name: String)
class Apple (name: String) extends Fruit(name)
class Orange(name: String) extends Fruit(name)
class BigOrange(name:String) extends Orange(name)
class BigFLOrange(name:String) extends BigOrange(name)
// Straight from the doc
trait Node[+B ] {
def prepend[U >: B ](elem: U)
}
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
def prepend[U >:B ](elem: U) = ListNode[U](elem, this)
def head: B = h
def tail = t
}
case class Nil[+B ]() extends Node[B] {
def prepend[U >: B ](elem: U) = ListNode[U](elem, this)
}
But this definition seems to allow multiple unrelated things in the same container
val f = new Fruit("fruit")
val a = new Apple("apple")
val o = new Orange("orange")
val bo = new BigOrange("big orange")
val foo :ListNode[BigOrange] = ListNode[BigOrange](bo, Nil())
foo.prepend(a) // add an apple to BigOrangeList
foo.prepend(o) // add an orange to BigOrangeList
val foo2 : ListNode[Orange] = foo // and still get to assign to OrangeList
So I am not sure this is a great example in the docs. And, question, how doe I modify the constraints so that..this behaves more like a List?
User #gábor-bakos points out that I am confusing invariance with covariance. So I tried the mutable list buffer. It does not later allow apple to be inserted into an Orange list Buffer, but it is not covariant
val ll : ListBuffer[BigOrange]= ListBuffer(bo)
ll += bo //good
ll += a // not allowed
So..can my example above (ListNode) be modified so that
1. it is covariant (it already is)
2 It is mutable, but mutable like the ListBuffer example (will not later allow apples to be inserted into BigOrange list
A mutable list cannot/should not be covariant in its argument type.
Exactly because of the reason you noted.
Suppose, you could have a MutableList[Orange], that was a subclass of a MutableList[Fruit]. Now, there is nothing that would prevent you from making a function:
def putApple(fruits: MutableList[Fruit], idx: Int) =
fruits(idx) = new Apple
You can add Apple to the list of Fruits, because Apple is a Fruit, nothing wrong with this.
But once you have a function like that, there is no reason you can't call it like this:
val oranges = new MutableList[Orange](new Orange, new Orange)
putApple(oranges, 0)
This will compile, since MutableList[Orange] is a subclass of MutableList[Fruit]. But now:
val firstOrange: Orange = oranges(0)
will crash, because the first element of oranges is actually an Apple.
For this reason, mutable collections have to be invariant in the element type (to answer the question you asked in the comments, to make the list invariant remove the + before B, and also get rid of the type parameter in prepend. It should just be def pretend(elem: B)).
How to get around it? The best solution is to simply not use mutable collections. You should not need them in 99% or real life scala code. If you think you need one, there is a 99% you are doing something wrong.
The major thing that you are probably missing is that prepend does not modify a list. In the line val foo2 : ListNode[Orange] = foo the list foo is still of type ListNode[BigOrange] and given covariance of parameters this assignment is valid (and there is nothing particularly awkward about it). Compiler will prevent you from assigning Fruits to Oranges (in this case assigning an Apple to an Orange is hazardous) but you have to save modified lists beforehand:
val foo: ListNode[Fruit] = ListNode[BigOrange](bo, Nil()).prepend(a).prepend(o)
val foo2: ListNode[Orange] = foo // this is not valid
Moreover your Node definition lacks return type for prepend - thus compiler infers wrong return type (Unit instead of ListNode[U]).
Here's fixed version:
trait Node[+B] {
def prepend[U >: B ](elem: U): Node[U]
}
I want to remove the following import:
import my.package.version.class1
the reason is that I want to pass the version as a parameter so I can have the following options:
my.package.version1.class1
my.package.version2.class1
my.package.version3.class1
When I do it hard-coded like that it works
classOf[my.package.version1.class1].getPackage
But I need it to be a String type so I can append the version each time.
val hh = "my.package."+versionParamater+".class1"
classOf[hh].getPackage //THIS WONT WORK error: identifier expected but string literal found.
I also tried doing this and it didnt work as well:
val pkg = Package.getPackage(" my.package.version1.class1");
can you please assist?
You will have to revert to the java.lang.Class.forName method to get hold of the class. But note that you will get back a Class<?> rather than a Class<class1>. For example:
scala> Class.forName("java.lang.String")
res0: Class[_] = class java.lang.String
You can cast it, though:
scala> res0.asInstanceOf[Class[String]]
res1: Class[String] = class java.lang.String
A mis-cast will not result in a ClassCastException:
scala> res0.asInstanceOf[Class[Integer]]
res2: Class[Integer] = class java.lang.String
You can use java.lang.Class.forName(fullClassPackage).
Example,
val versionParamater = "version1"
val fullPackage = "my.package."+versionParamater+".class1"
val versionedClass = Class.forName(fullPackage)
assert(versionedClass.getSimpleName == "class1")
assert(versionedClass.getPackage.getName == "my.package.version1")
This is a simplified example but the problem remains the same.
I want to achieve this using macros (scala based pseudocode):
(a: Int) => {
val z = "toShort"
a.z
}
If I reify it, I would obtain something similar to this:
Function(
List(
ValDef(
Modifiers(Flag.PARAM),
newTermName("a"),
Ident(scala.Int),
EmptyTree
)
),
Block(
List(
ValDef(
Modifiers(),
newTermName("z"),
TypeTree(),
Literal(Constant("toShort"))
)
),
Apply(
Select(
Ident(newTermName("a")),
newTermName("toShort")
),
List()
)
)
)
I dont know how to access to a value and then use it as a TermName.
I tried replacing newTermName("toShort") with newTermName(c.Expr[String](Select(Ident(newTermName("z")))).splice) but the compiler doesn't seem to like:
exception during macro expansion:
java.lang.UnsupportedOperationException: the function you're calling has not been spliced by > the compiler.
this means there is a cross-stage evaluation involved, and it needs to be invoked explicitly.
if you're sure this is not an oversight, add scala-compiler.jar to the classpath,
import scala.tools.reflect.Eval and call <your expr>.eval instead.
I've also tried 'eval' as suggested by compiler: newTermName(c.eval(c.Expr[String](...)) but neither worked.
How could I convert a tree like Select(Ident(newTermName("z"))) (which is a access to a value of a local val) to a Name a string which can be used as a parameter for newTermName? Is it possible?
UPDATE:
Here the real problem brought to you as a gist!
Thanks in advance,
I have a hard time understanding what you're trying to achieve, and why you are using Trees everywhere. Trees are really low level, hard to use, tricky, and it is very difficult to understand what the code does. Quasiquotes (http://docs.scala-lang.org/overviews/macros/quasiquotes.html) are the way to go indeed and you can use them on scala 2.10.x production release thanks to the macro paradise plugin (http://docs.scala-lang.org/overviews/macros/paradise.html). The you can simply write q"(a: Int) => {val z = "toShort"; a.z}" and you directly get the tree expression you just typed.
To answer your question, the first point is to remember that macros are evaluated at compile time. They therefore can not generate code which depends on a runtime value. This is why the compiler is complaining about your splice. But if you pass a value which can be computed at compile time, typically a literal, then you can use eval to get its value within your macro code. Eval does suffer a bug though, as indicated in scaladoc. It should only be called on untyped trees. So the way to call eval on an s: c.Expr[String] expression would be val s2 = c.eval(c.Expr[String](c.resetAllAttrs(c.tree.duplicate))) which gives you a String you can then use normally in your code, for instance q"(a: Int) => a.${newTermName(s2)}".
To put it all together, let's imagine you to create a macro that'll output a string value from an object and one of its String field. It'll give something like
def attr[A](a: A, field: String): String = macro attrImpl[A]
def attrImpl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A], field: c.Expr[String]) = {
import c.universe._
val s = c.eval(c.Expr[String](c.resetAllAttrs(field.tree.duplicate)))
c.Expr[String](q"a.${newTermName(s)}")
}
REPL session test:
scala> object a { val field1 = "field1"; val field2 = "field2" }
defined module a
scala> attr(a, "field1")
res0: String = field1
scala> attr(a, "field2")
res1: String = field2
To understand the difference between compile time and runtime, you can meditate about the following result in REPL ;-)
scala> val s = "field1"; attr(a, s)
error: exception during macro expansion:
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
$iw is not an enclosing class
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.throwIfErrors(ToolBoxFactory.scala:311)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:244)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:408)
at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:411)
at scala.reflect.macros.runtime.Evals$class.eval(Evals.scala:16)
at scala.reflect.macros.runtime.Context.eval(Context.scala:6)
at .attrImpl(<console>:14)
scala> val s = "field1"
s: String = field1
scala> attr(a, s)
res3: String = field1
Hope it helps ;))
I'm trying to get the classOf[the-abstract-class-Option], but instead I always get the classOf[the-Option-*object*]. How can I get the class of the abstract class instead?
Both Option.getClass and classOf[Option[_]] gives me class scala.Option$.
Edit: I needn't have asked this; all of a sudden, classOf[Option[_]] works fine, weird. /Edit
Background:
I'm trying to invoke via reflection a method that takes an Option[String] parameter.
It signature look like so: ...(..., anySectionId: Option[String], ...)...
Before I can invoke the method, I look it up via getDeclaredMethod. But to do that, I need a list of parameter types, which I construct by calling _.getClass on each argument I'm going to give to the method. But _.getClass returns classOf[None] or classOf[Some] for Option instances, which makes getDeclaredMethod fail, because (?) the signature is based on Option not Some/None.
Here's the code:
val clazz: Class[_] = Play.current.classloader.loadClass(className)
val paramTypes = arguments.map(_ match {
case None => Option.getClass // gives me the object, not the abstract class
case _: Some[_] => classOf[Option[_]] // this also gives me the object :-(
case x => x.getClass // results in None or Some for Option instances
})
val m: jl.reflect.Method = clazz.getDeclaredMethod("apply", paramTypes: _*)
and the last line above fails for a method with any Option parameter (otherwise everything works fine).
The best way is use Scala reflection.
The next best way is not to make work for yourself by trying to match the param types.
Using getClass fails for subtypes:
scala> class Foo
defined class Foo
scala> class Bar extends Foo
defined class Bar
scala> class Baz { def baz(f: Foo) = 1 }
defined class Baz
scala> val b = new Baz
b: Baz = Baz#d33eaa9
scala> val p = new Bar
p: Bar = Bar#406c5ca2
scala> classOf[Baz].getDeclaredMethod("baz", p.getClass)
java.lang.NoSuchMethodException: Baz.baz(Bar)
It's easier just to match on the name:
scala> classOf[Baz].getMethods.find(_.getName == "baz") map (_.invoke(b,p)) getOrElse -1
res5: Any = 1
or filter on the number of params for poor man's overloading resolution, then maybe filter on all args having conforming types.
The notation for accidentally getting the object is in fact:
scala> classOf[Option$]
res8: Class[Option$] = class scala.Option$
Answer: classOf[Option[_]]
Weird! Suddenly classOf[Option[_]] works. I feel sure I tested once or twice before I submitted the question :-( Perhaps the IDE didn't have time to save the file before I recompiled, weird.
I don't know if I should delete the question. Or perhaps I should leave it as is, in case classOf[Option[_]] isn't obvious to everyone.
My code looks like this:
val people = Array(Array("John", "25"), Array("Mary", "22"))
val headers = Seq("Name", "Age")
val myTable = new Table(people, headers)
I get this syntax error:
overloaded method constructor Table with alternatives:
(rows: Int,columns: Int)scala.swing.Table
<and>
(rowData: Array[Array[Any]],columnNames: Seq[_])scala.swing.Table
cannot be applied to
(Array [Array[java.lang.String]], Seq[java.lang.String])
I don't see why the second alternative isn't used. Is there a distinction between "Any" and "_" that's tripping me up here?
As Kim already said, you need to make your array covariant in his element type, because Scala's Arras are not covariant like Java's/C#'s.
This code will make it work for instance:
class Table[+T](rowData: Array[Array[T]],columnNames: Seq[_])
This just tells the compiler that T should be covariant (this is similar to Java's ? extends T or C#'s out T).
If you need more control about what types are allowed and which not, you can also use:
class Table[T <: Any](rowData: Array[Array[T]],columnNames: Seq[_])
This will tell the compiler that T can be any subtype of Any (which can be changed from Any to the class you require, like CharSequence in your example).
Both cases work the same in this scenario:
scala> val people = Array(Array("John", "25"), Array("Mary", "22"))
people: Array[Array[java.lang.String]] = Array(Array(John, 25), Array(Mary, 22))
scala> val headers = Seq("Name", "Age")
headers: Seq[java.lang.String] = List(Name, Age)
scala> val myTable = new Table(people, headers)
myTable: Table[java.lang.String] = Table#350204ce
Edit: If the class in question is not in your control, declare the type you want explicitly like this:
val people: Array[Array[Any]] = Array(Array("John", "25"), Array("Mary", "22"))
Update
This is the source code in question:
// TODO: use IndexedSeq[_ <: IndexedSeq[Any]], see ticket [#2005][1]
def this(rowData: Array[Array[Any]], columnNames: Seq[_]) = {
I wonder if someone forgot to remove the workaround, because #2005 is fixed since May 2011 ...
Array[Array[String]] is not a subtype of Array[Array[Any]] because Array's type parameter is not covariant. You should read up on co-, contra- and invariance. This should fix it:
val people =
Array(Array("John", "25"), Array("Mary", "22")).asInstanceOf[Array[Array[Any]]