Consider the following snippet. += is not a member of java.lang.String, so I guess there is some sort of Implicit conversion going on. How do I find a list of such predefined implicit conversions acting on String?
scala> var x = "asdf"
x: java.lang.String = asdf
scala> x += x
scala> x
res2: java.lang.String = asdfasdf
You picked a particularly bad example. += is, in a sense, part of String. See this comment on the Javadoc for java.lang.String:
The Java language provides special support for the string
concatenation operator ( + ), and for conversion of other objects to
strings.
You'll have to look up Java language specification to find more information about it (15.18.1). But, then again, Scala is not Java, so + is also part of the Scala language specification (12.3.1).
So far I have spoken of +, not +=. However, Scala has a special syntactic sugar for assignment. As described in section 6.12.4, except for <=, >=, != and operators starting with an =, any operator symbol (see "operator characters" in chapter 1) that ends in an equal sign will be reinterpreted if it does not exist as a method. Specifically,
x += 1
will be reinterpreted as
x = x + 1
That will happen regardless of whether x is a var, so one might occasionally see an error message "reassignment to val".
So, as you can see, += is really part of String, through an exception in Java specification that was replicated in Scala specification, plus a bit of syntactic sugar.
Which doesn't mean there aren't methods not in java.lang.String that can be used with it through implicit conversions. I'll leave that to the other answers, however. If I were you, I'd change the method in the question, to make it correct. Besides, += is unsearchable in Stack Overflow.
You need to look in scala.Predef - all implicits that are defined there would be always in scope (so you don't need to import them).
If you look in it's source code, you will find this section:
// Strings and CharSequences ------------
...
implicit def augmentString(x: String): StringOps = new StringOps(x)
implicit def unaugmentString(x: StringOps): String = x.repr
...
StringOps defines implicit conversions for String. It gets imported into the scope in scala.Predef along with other useful things.
Not specific to String, but the Scala REPL has a neat feature to see all the implicits in scope:
scala> :implicits
No implicits have been imported other than those in Predef.
scala> :implicits -v
/* 96 implicit members imported from scala.Predef */
/* 66 inherited from scala.Predef */
implicit def Double2double(x: jl.Double): Double
implicit def byte2double(x: Byte): Double
implicit def char2double(x: Char): Double
...
/* 30 inherited from scala.LowPriorityImplicits */
implicit def genericWrapArray[T](xs: Array[T]): mutable.WrappedArray[T]
implicit def wrapBooleanArray(xs: Array[Boolean]): mutable.WrappedArray[Boolean]
implicit def wrapByteArray(xs: Array[Byte]): mutable.WrappedArray[Byte]
...
It's also worth noting that implicits don't have to be in scope to be applied. For example, we can define an implicit conversion in a companion object,
case class Foo(s: String)
object Foo { implicit def string2Foo(s: String) = Foo(s.reverse) }
and then apply it, even though its not in scope,
scala> val f: Foo = "hello"
f: Foo = Foo(olleh)
The Foo companion object is searched for implicits because the target type is Foo. For more info, see Daniel Sobral's answer to: Where does Scala look for implicits?
Related
I have the following snippet that (I think) defines a method addNumber1(x:T):T on a generic type T which is a subtype of AnyVal and has a method +(s:Int):T.
def addNumber1[T <: AnyVal {def +(s:Int):T}](x:T):T = {x + 1}
addNumber1(31) // compiles but throws exception
java.lang.NoSuchMethodException: java.lang.Integer.$plus(int)
at java.lang.Class.getMethod(Class.java:1786)
at .reflMethod$Method1(<console>:8)
at .addNumber1(<console>:8)
... 33 elided
I tried adding import scala.language.reflectiveCalls to suppress a feature warning but still get the error.
I am able to use this when working with AnyRef or Any as below:
def addNumber1[T <: Any {def +(s:Int):T}](x:T):T = {x + 1}
class Foo(s:String) {def +(i:Int) = new Foo((s+1).toString)} // random code
class Bar(s:Foo) {def +(i:Int) = new Bar(new Foo(i.toString))} // random code
addNumber1(new Foo("1")) // works
addNumber1(new Bar(new Foo("1"))) // works
addNumber1(1) // compiles but gives exception
You run into an intersection of quite a few features:
So far as the initial stages of Scala compiler are concerned (including typechecking), Int does have an (overloaded) + method. But this "method" is treated specially by the later stages (as are all methods on Int, because it isn't really a class).
Methods called + and defined in Scala are translated to methods called $plus in bytecode, since + is not a legal identifier there. Since + on Int is special, as mentioned above, this doesn't apply to it. Since the structural types are implemented using Java reflection, your addNumber1 looks somewhat like
def addNumber1(x: Object) = x.getClass.getMethod("$plus").invoke(x, 1)
To call addNumber1 on an int, it has to be boxed to Integer first, because int is not an object. Integer, not being a Scala type, doesn't have a $plus method. In Scala you can write something like val x: Integer = ...; x + 1, but this uses an implicit conversion which Java reflection has no idea about.
I think the problem has nothing to do with AnyVal, AnyRef Or Any.
addNumber1(new Foo("1"))
This works because you indeed defined a Foo class that provides an implementation of def +(s:Int):T.
addNumber1(1)
This doesn't work because Integer class doesn't provide it, as is mentioned in the exception:
java.lang.NoSuchMethodException: java.lang.Integer.$plus(int)
From Magnet Pattern article, I found implicit conversion special pattern.
What is interesting is that this approach also works for “overloads” with more than one parameter just as well as different return types. If you call complete with several arguments the compiler looks for an implicit conversion that can produce a magnet instance from a tuple wrapping all arguments. This way overloads with up to 22 parameters (the maximum arity of tuples in scala) can be supported.
scala> def printInt(i: Int) = println(i)
printInt: (i: Int)Unit
scala> printInt(10)
10
scala> printInt(10, 20)
^
error: too many arguments (2) for method printInt: (i: Int)Unit
scala> implicit def toOneInt(is: (Int, Int)): Int = is._1 + is._2
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
toOneInt: (is: (Int, Int))Int
scala> printInt((10, 20))
30
scala> printInt(10, 20)
30
printInt((10, 20)) is boring me, but printInt(10, 20) is awesome!
Then I sought the specification of this special pattern from Scala Language Specification (2.12).
However I cannot find it yet and finally give up.
Could anyone show me where is the special pattern from Scala Language Specification (2.12) ?
By xuwei_k help, finally I reached name of the feature "adapted args" or "auto tupling".
For further details, Scala Puzzle(jp) was so helpful.
And also Scala Puzzlers shows [SI-3583] Spec doesn't mention automatic tupling - Scala.
Then, the answer is nowhere yet.
I'm defining some Scala implicits to make working with a particular unchangeable set of Java classes easier. The following Scala code is a simplified example that obviously looks crazy, in the real world I'm trying to grab particular resources (rather than numeric age) implicitly from the Monkey, Tree & Duck for use in various methods like purchaseCandles():
// actually 3 Java classes I can not change:
case class Monkey(bananas: Int)
case class Tree(rings: Int)
case class Duck(quacks: Seq[String])
// implicits I created to make my life easier...
implicit def monkey2Age(monkey: Monkey): Int = monkey.bananas / 1000
implicit def tree2Age(tree: Tree): Int = tree.rings
implicit def duck2Age(duck: Duck): Int = duck.quacks.size / 100000
// one of several helper methods that I would like to define only once,
// only useful if they can use an implicit parameter.
def purchaseCandles()(implicit age: Int) = {
println(s"I'm going to buy $age candles!")
}
// examples of usage
{
implicit val guest = Monkey(10000)
purchaseCandles()
}
{
implicit val guest = Tree(50)
purchaseCandles()
}
{
implicit val guest = Duck(Seq("quack", "quack", "quack"))
purchaseCandles()
}
The compiler error, which occurs 3 times:
could not find implicit value for parameter age: Int
purchaseCandles()
^
Leaving aside the many different ways in which this sample code is crazy, my real question is: can implicit conversions of implicit values satisfy implicit parameters in Scala?
Short answer: no. Scala's compiler will only ever look to apply a single implicit, so if it fails to spot an implicit int lying around, it will stop and give up.
However, you could write your purchaseCandles method to operate on types that can be converted to an Int, and require a parameter of that type:
def purchaseCandles[A <% Int]()(implicit age : A) = {
val asAge : Int = age
println(s"I'm going to buy $asAge candles!")
}
The asAge part is necessary to force the application of the implicit conversion.
As of yet, I seem to need to specify the type of A in this scenario, though I can't work out why: since there shouldn't be other values around of types that can be implicitly converted to Int (this happens with brand new types as well, so it's not the ubiquity of Int.) But you can do:
{
implicit val guest = Monkey(10000)
purchaseCandles[Monkey]()
}
This use of implicits, however, is probably a bad idea!
You actually can do that: You just have to mark the parameters of your implicit conversion as implicit as well:
implicit def monkey2Age(implicit monkey: Monkey): Int = monkey.bananas / 1000
implicit def tree2Age(implicit tree: Tree): Int = tree.rings
implicit def duck2Age(implicit duck: Duck): Int = duck.quacks.size / 100000
This will chain the implicits they way you want.
As always: Beware, it will also do so in places you don't want it to. By the way, I strongly advise against an implicit parameter of type Int (or an implicit value thereof). It is just too generic. (I'm somewhat assuming this is just like that in your example).
Main.scala:
package controler
object Main {
def main(args: Array[String]) {
import Utilites._
isJavaUpToDate
}
}
Utilites.scala:
package controler
object Utilities {
def isJavaUpToDate = {
val javaVersion = augmentString(System.getProperty("java.version").substring(2, 3))
javaVersion >= 6
}
}
Why isn't this working?
I have been trought a bunch of differenet tutorial sites where this works no problem.
I always says that val Utilites cannot be found.
P.S. Why does it keep sugesting me to change .toInt with augmentString() when it just breaks the code?
Now this gives me trouble, something about implicit ordering and method orderTOOrdered.
In your main method you've typed Utilites where you meant to type Utilities.
Correct: Utilities
Wrong: Utilites
Note the missing i :) And because your brain is a powerful spelling correction tool, it pretended like the spelling was correct. The Scala compiler isn't as cool, though ;)
Also, for me, the following does not work:
scala> augmentString(System.getProperty("java.version").substring(2, 3)) >= 5
<console>:15: error: No implicit Ordering defined for AnyVal.
augmentString(System.getProperty("java.version").substring(2, 3)) >= 5
Instead I replaced it with
System.getProperty("java.version").substring(2, 3)).toInt >= 5
The implicit ordering issue you experienced with the former code is because Scala does not know how to apply the >= method/operator to the type scala.collection.immutable.StringOps, which augmentString() returns.
Note that by calling augmentString you're explicitly transforming your string to a StringOps.
StringOps does define a >= method, but it's meant to compare strings (its signature is def >=(that: String): Boolean )
If you want to compare Ints you should use the toInt method defined in StringOps.
def isJavaUpToDate = {
val javaVersion = augmentString(System.getProperty("java.version").substring(2, 3)).toInt
javaVersion >= 6
}
Also, unless you need to disambiguate the toInt against another implicit that you defined (or is defined somewhere else in a library you're using) there should be no need to call augmentString explicitly. The following should just work (unless the compiler tells you it does not) and it should implicitly have the same effect as the code above of transforming your String to a StringOps.
def isJavaUpToDate = {
val javaVersion = System.getProperty("java.version").substring(2, 3)
javaVersion.toInt >= 6
}
EDIT: as per #som-snytt's comment
The error you're getting (No implicit Ordering defined for AnyVal) is due to the compiler reasoning more or less like this:
javaVersion >= 6 means javaVersion.>=(6), i.e. I must look for a method called >= on javaVersion that takes an integer
javaVersion is a StringOps... there is a >= method in StringOps (courtesy of the StringLike trait it's extending, that in turn extends Ordered[String]) but it takes a String argument, not an Int
let's see if I have an implicit in scope that supplies a suitable Ordering for StringOps. Now, since you're trying to compare a String with a Int you're looking at an Ordering that can compare two values with the nearest common ancestor that can contain both String and Int, i.e. AnyVal [EDIT: although String is an AnyRef so I don't really get this part...].
No luck there... give up with an error that says that no implicit Ordering was found comparing AnyVals
In my app, I am keeping track of the number of credits the user has. To add some type checking, I'm using a Credits class similar to this one:
case class Credits(val numCredits: Int) extends Ordered[Credits] {
...
}
Suppose I have a function def accept(creds: Credits): Unit that I want to call. Would there be a way for me to call it with
process(Credits(100))
process(0)
but not with this?
process(10)
I.e., I'd like to provide an implicit conversion only from the literal 0 and none other. Right now, I just have val Zero = Credits(0) in the companion object and I think that's rather good practice, but I'd be interested in an answer anyway, including other comments, like:
could this be done with macro implicit conversions in 2.10?
should Credits rather extend AnyVal and not be a case class in 2.10?
This kind of compile-time checking are the good terrain to use macros, which will be available in 2.10
A very smart guy named Jason Zaugg has already implemented something similar to what you need, but it applies to regex: Regex Compile Time checking.
You might want to look to its Macrocosm to see how it is done and how you could code your own macros with the same purpose.
https://github.com/retronym/macrocosm
If you really want to know more about Macros, firstly I would say that you need to be brave because the documentation is scarce for now and the API is likely to change. Jason Zaugg works compiles fine with 2.10-M3 but I am not sure it will works with the newer version.
If you want to start with some readings:
A good entry point is the scalamacros website http://scalamacros.org/ and the SIP document https://docs.google.com/document/d/1O879Iz-567FzVb8kw6N5OBpei9dnbW0ZaT7-XNSa6Cs/edit?pli=1
If you have time, you might also wants to read Eugene Burmako presentation:
http://scalamacros.org/talks/2012-04-28-MetaprogrammingInScala210.pdf
Now, getting to the topic, Scala macros are CATs : "Compile-time AST Transformations".
The abstract syntax tree is the way the compiler represents your source code. The compiler applies consequent transformations to the AST and at the last step it actual generates the java bytecode.
Let's now look to Jason Zaugg code:
def regex(s: String): scala.util.matching.Regex = macro regexImpl
def regexImpl(c: Context)(s: c.Expr[String]): c.Expr[scala.util.matching.Regex] = {
import c.universe._
s.tree match {
case Literal(Constant(string: String)) =>
string.r // just to check
c.reify(s.splice.r)
}
}
As you seen regex is a special function which takes a String and returns a Regex, by calling macro regexImpl
A macro function receives a context in the first parameter lists, and in second argument list the parameters of the macro under the form of c.Expr[A] and returns a c.Expr[B]. Please note that c.Expr is a path dependent type, i.e. it is a class defined inside the Context, so that if you have two context the following is illegal
val c1: context1.Expr[String] = ...
val c2: context2.Expr[String] = ...
val c3: context1.Expr[String] = context2.Expr[String] // illegal , compile error
Now if you look what happens in the code:
There is a match block which matches on s.tree
If s.tree is a Literal, containing a constant String , string.r is called
What's going on here is that there is an implicit conversion from string to StringOps defined in Predef.scala, which is automatically imported in the compilation every scala source
implicit def augmentString(x: String): StringOps = new StringOps(x)
StringOps extends scala.collection.immutable.StringLike, which contains:
def r: Regex = new Regex(toString)
Since macros are executed at compile time, this will be executed at compile time, and compilation will fail if an exception will be thrown (that is the behaviour of creating a regex from an invalid regex string)
Note: unluckily the API is very unstable, if you look at http://scalamacros.org/documentation/reference.html you will see a broken link towards the Context.scala. The right link is https://github.com/scala/scala/blob/2.10.x/src/reflect/scala/reflect/makro/Context.scala
Basically, you want dependent types. Why Scala supports a limited form of dependent types in path dependent types, it can't do what you ask.
Edmondo had a great idea in suggesting macros, but it has some limitations. Since it was pretty easy, I implemented it:
case class Credits(numCredits: Int)
object Credits {
implicit def toCredits(n: Int): Credits = macro toCreditsImpl
import scala.reflect.makro.Context
def toCreditsImpl(c: Context)(n: c.Expr[Int]): c.Expr[Credits] = {
import c.universe._
n.tree match {
case arg # Literal(Constant(0)) =>
c.Expr(Apply(Select(Ident("Credits"), newTermName("apply")),
List(arg)))
case _ => c.abort(c.enclosingPosition, "Expected Credits or 0")
}
}
}
Then I started up REPL, defined accept, and went through a basic demonstration:
scala> def accept(creds: Credits) { println(creds) }
accept: (creds: Credits)Unit
scala> accept(Credits(100))
Credits(100)
scala> accept(0)
Credits(0)
scala> accept(1)
<console>:9: error: Expected Credits or 0
accept(1)
^
Now, to the problem:
scala> val x = 0
x: Int = 0
scala> accept(x)
<console>:10: error: Expected Credits or 0
accept(x)
^
In other words, I can't track properties of the value assigned to identifiers, which is what dependent types would allow me to do.
But the whole strikes me as wasteful. Why do you want just 0 to be converted? It seems you want a default value, in which case the easiest solution is to use a default value:
scala> def accept(creds: Credits = Credits(0)) { println(creds) }
accept: (creds: Credits)Unit
scala> accept(Credits(100))
Credits(100)
scala> accept()
Credits(0)
Use could use an implicit partial function:
scala> case class Credits(val numCredits: Int)
defined class Credits
scala> def process(c: Credits) = {}
process: (c: Credits)Unit
scala> implicit def i2c:PartialFunction[Int, Credits] = { case 0 => Credits(0) }
i2c: PartialFunction[Int,Credits]
Allows you
scala> process(Credits(12))
and
scala> process(0)
But:
scala> process(12)
scala.MatchError: 12 (of class java.lang.Integer)
at $anonfun$i2c$1.apply(<console>:9)
at $anonfun$i2c$1.apply(<console>:9)
at .<init>(<console>:12)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.sca
la:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:4
3)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Unknown Source)
Edit: But yes, the compiler will still allow process(12) resulting in a match error at runtime.