I'm trying out some things in Scala, coming from Python. Since Scala is a lot more strict about keeping types consistent, I was surprised to find out that I can do the following concatenation, which would blow up in Python:
def adder(one:Any, two:String) = {one+two}
adder("word", "suffix")
res13: String = wordsuffix
But also:
val x:Int = 1
adder(x, "suffix")
res12: String = 1suffix
So it just transforms an Int into a String w/out telling me. What is this called and what is the logic behind it?
And what is the benefit of this? I feel it can come back to bite me, e.g. when dealing with user input to a function.
I know this is not very specific and if this is too broad, I'll gladly retract the question.
There is an implicit class in scala.Predef that operates on objects of any type
implicit final class any2stringadd[A](private val self: A) extends AnyVal {
def +(other: String): String = String.valueOf(self) + other
}
That implements Any + String (as you have defined it in adder). As rogue-one mentioned, there is also a method for concatenating String + Any defined in StringOps. If you tried to do Any + Any it would fail because it's expecting a String as the argument.
So it just transforms an Int into a String w/out telling me
Scala is converting your Int into a String, but it's not a type conversion because Int cannot be coerced into a String. You can observe that by trying something like this:
def foo(str: String) = ???
foo(5) // Type mismatch: expected: String, actual: Int
That will fail to compile because Scala can't magically coerce an Int into a String.
what is the logic behind it?
See implicit classes
And what is the benefit of this? I feel it can come back to bite me, e.g. when dealing with user input to a function.
It's a convenience method that's very specific to String and concatenation. This feature is implemented in Java, so I believe it was implemented in Scala to maintain source compatibility. My example above shows that (except in this specific case), user input to a function will respect the types defined on the function.
It's called implicit conversion (or implicit typecasting)
The purpose - convenience so you don't have to do everything manually. Most high-level languages will do that with the most used generics like strings, ints, bools...
You can check scala.Predef to see all the methods used when implicitly converting types, and you can take control of it using scala.language.implicitConversions. Read more at Scala Documentation.
This String concatenation not only works for Int but for any data-type. For instance
scala> case class Elephant(value: String)
defined class Elephant
scala> "Hello" + Elephant("elephant")
res2: String = HelloElephant(elephant)
This is because of the method + defined in StringOps (via Predef) class that accepts argument of type Any. So it is a method that is made available to a String object via implicit conversion that takes an argument of type Any. so "Hello" + Elephant("elephant") is actually "Hello".+(Elephant("elephant"))
Related
I m trying to declare function in trait that takes variable number of argument and during implementation of the trait I would expand the number of arguments. How can this done in Scala
I am expecting to come up with code like below.
trait Column {
def rule
}
case object FirstColumn extends Column{
def rule(s: String) : String
}
case object SecondColumn extends Column{
def rule(s1: String, s2: String) : String
}
I have tried using Strings* , but it is not allowing me to expand my number of arguments during implementation. I understand there are various way to handle this problem but i am specifically looking to have above signature for my team to write functions.
This is primarily expanding on my comment on the question. This answer gets you about as close as Scala lets you get to what you want, but it also shows why it's probably not a good idea to do what you're doing.
You can express (something close to) the type you want, but I'm not sure what you intend to gain. First, if you want to take different arglist types, then Column needs to be generic.
trait Column[-A] {
def rule(arg: A): String
}
Then we can implement your case objects as subclasses of an appropriate parameterization of this.
case object FirstColumn extends Column[String] {
def rule(arg: String): String =
"stub implementation"
}
case object SecondColumn extends Column[(String, String)] {
def rule(arg: (String, String)): String =
"stub implementation"
}
Note that FirstColumn and SecondColumn do not inherit from the same Column[A] as they don't implement the same method. We can get them to have a common type, but... not in a very useful way.
One option is to find a common supertype of Column[String] and Column[(String, String)], which (since the argument is contravariant) is akin to finding a common subtype of String and (String, String). The closest common subtype is... Null. That's not helpful unless you're only ever planning to pass null to your rule.
Instead, we can use existentials.
val foo: Column[_] = FirstColumn
val bar: Column[_] = SecondColumn
Now we've lost all type information. You can access the foo.rule slot and you can print it, but you can't call it because we don't know what we need to pass it. You'll have to do a cast to get it back to a usable format.
The point that I'm making here is that, yes, it's doable, but once you've lost as much type information as you're giving up, there's not much point. The type system is correctly telling us that foo and bar have virtually nothing in common except the existence of a method named rule which takes... some kind of argument. From a type theory perspective, it's hard to get more uninteresting than that.
How do I write shorthand for a datatype?
For example.
lets say instead of List[Integer], I would rather type Integers
instead of this
def processNumbers(input:List[Integer]):List[Integer] = ...
to
def processNumbers(input:Integers):Integers = ...
Is this possible?
Thanks
Yes, you can do this with a type alias.
type Integers = List[Int] // scala.Int is preferred over java.lang.Integer
That being said, this isn't really a good use for them. List[Int] is very clear to other scala developers, wheres your type Integers provides no extra information and so will detract from the readability of your code over time.
A use of type aliases that would improve your code's readability though would be something like
type UserId = Int
def processUsers(ids: List[UserId]): Foo
In this case it provides extra information to the reader vs
def processUsers(ids: List[Int]): Foo
Using that kind of type alias also will allow you to gradually make your code more type-safe over time by changing the definition from a type alias to a value class.
case class UserId(value: Int) extends AnyVal
You won't need to change the method signatures of anything already having "UserId", but this will let the compiler assist you in making sure you don't do something like
val ids: List[Int] = getBlogPostIds()
val foo = processUsers(ids) // Oops, those Ints are for blog posts, not users
Using the value class approach, a mistake like that becomes a compiler error. Used pervasively it adds quite a lot of guidance in writing correct code.
val ids: List[BlogPostId] = getBlogPostIds
val foo = processUsers(ids) // Compile error; BlogPostId != UserId
If I try to define an implicit conversion for a primitive type, then it doesn't seem to work. E.g.:
implicit def globalIntToString(a: Int) : String = { a.toString() + "globalhi" }
1.toInt + "hi"
The above will still return simply "1hi" as the result.
However, it seems that if I parameterize a class or a def and then pass in the implicit for the parametrized case, then it seems to work. Does anyone know what the reasons are? E.g. does this have something to do with boxing/unboxing of primtives (e.g., parameterized primitives are boxed)? Does implicit only work with reference types and not primitive types?
class typeConv[T] { implicit def tToStr(a: T) : String = { a.toString() + "hi" } }
class t[K](a: K)(tc : typeConv[K]) { import tc._; println(a + "cool"); println(1.toInt + "cool" ) }
new t(1)(new typeConv[Int])
There are a few subtle things going on here. #yan has already explained the core issue - I'll try to add some more specific information.
As noted, 1.toInt + "hi" will never use any implicit conversion because Scala Int class actually has a method + that takes a String parameter. The compiler will look for an implicit view only when it can't find matching member in the original type.
A little more complicated stuff is happening inside your t class. Scalac will look for implicit conversion from a generic type K to any type that has a + method that takes a String parameter. There will be two candidates for such a conversion: your own tc.tToStr and Scala built-in scala.Predef.any2stringadd.
Normally, any2stringadd would be used, but in your example, your own conversion is used. Why does it have precedence over any2stringadd?
During implicit search, tc.tToStr is seen as a function of type K => String, while any2stringadd is seen as a function of type Any => StringAdd. My guess is that K => String is seen by the compiler as a more specific conversion than Any => StringAdd, but someone would have to confirm it with a proper reference to Scala Language Specification.
As you can see, defining such conversions may cause you a lot of strange behaviour. I'd definitely say that introducing an implicit conversion to a String is asking for trouble.
This happens because Scala defines a + operator on the Int type that takes a String and does not need to resolve an implicit conversion. Also, converting to String is usually a bad idea, as you'd generally have a custom, one-off type that would define the methods you're trying to add.
EDIT
OK, #Drexin brings up a good point re: loss of type safety/surprising results when using implicit converters.
How about a less common conversion, where conflicts with PreDef implicits would not occur? For example, I'm working with JodaTime (great project!) in Scala. In the same controller package object where my implicits are defined, I have a type alias:
type JodaTime = org.joda.time.DateTime
and an implicit that converts JodaTime to Long (for a DAL built on top of ScalaQuery where dates are stored as Long)
implicit def joda2Long(d: JodaTime) = d.getMillis
Here no ambiguity could exist between PreDef and my controller package implicits, and, the controller implicits will not filter into the DAL as that is in a different package scope. So when I do
dao.getHeadlines(articleType, Some(jodaDate))
the implicit conversion to Long is done for me, IMO, safely, and given that date-based queries are used heavily, I save some boilerplate.
Similarly, for str2Int conversions, the controller layer receives servlet URI params as String -> String. There are many cases where the URI then contains numeric strings, so when I filter a route to determine if the String is an Int, I do not want to stringVal.toInt everytime; instead, if the regex passes, let the implicit convert the string value to Int for me. All together it would look like:
implicit def str2Int(s: String) = s.toInt
get( """/([0-9]+)""".r ) {
show(captures(0)) // captures(0) is String
}
def show(id: Int) = {...}
In the above contexts, are these valid use cases for implicit conversions, or is it more, always be explicit? If the latter, then what are valid implicit conversion use cases?
ORIGINAL
In a package object I have some implicit conversions defined, one of them a simple String to Int:
implicit def str2Int(s: String) = s.toInt
Generally this works fine, methods that take an Int param, but receive a String, make the conversion to Int, as do methods where the return type is set to Int, but the actual returned value is a String.
Great, now in some cases the compiler errors with the dreaded ambiguous implicit:
both method augmentString in object Predef of type (x: String)
scala.collection.immutable.StringOps and method str2Int(s: String) Int
are possible conversion functions from java.lang.String to ?{val
toInt: ?}
The case where I know this is happening is when attempting to do manual inline String-to-Int conversions. For example, val i = "10".toInt
My workaround/hack has been to create an asInt helper along with the implicits in the package object: def asInt(i: Int) = i and used as, asInt("10")
So, is implicit best practice implicit (i.e. learn by getting burned), or are there some guidelines to follow so as to not get caught in a trap of one's own making? In other words, should one avoid simple, common implicit conversions and only utilize where the type to convert is unique? (i.e. will never hit ambiguity trap)
Thanks for the feedback, implicits are awesome...when they work as intended ;-)
I think you're mixing two different use cases here.
In the first case, you're using implicit conversions used to hide the arbitrary distinction (or arbitrary-to-you, anyway) between different classes in cases where the functionality is identical. The JodaTime to Long implicit conversion fits in that category; it's probably safe, and very likely a good idea. I would probably use the enrich-my-library pattern instead, and write
class JodaGivesMS(jt: JodaTime) { def ms = jt.getMillis }
implicit def joda_can_give_ms(jt: JodaTime) = new JodaGivesMS(jt)
and use .ms on every call, just to be explicit. The reason is that units matter here (milliseconds are not microseconds are not seconds are not millimeters, but all can be represented as ints), and I'd rather leave some record of what the units are at the interface, in most cases. getMillis is rather a mouthful to type every time, but ms is not too bad. Still, the conversion is reasonable (if well-documented for people who may modify the code in years to come (including you)).
In the second case, however, you're performing an unreliable transformation between one very common type and another. True, you're doing it in only a limited context, but that transformation is still liable to escape and cause problems (either exceptions or types that aren't what you meant). Instead, you should write those handy routines that you need that correctly handle the conversion, and use those everywhere. For example, suppose you have a field that you expect to be "yes", "no", or an integer. You might have something like
val Rint = """(\d+)""".r
s match {
case "yes" => println("Joy!")
case "no" => println("Woe!")
case Rint(i) => println("The magic number is "+i.toInt)
case _ => println("I cannot begin to describe how calamitous this is")
}
But this code is wrong, because "12414321431243".toInt throws an exception, when what you really want is to say that the situation is calamitous. Instead, you should write code that matches properly:
case object Rint {
val Reg = """([-]\d+)""".r
def unapply(s: String): Option[Int] = s match {
case Reg(si) =>
try { Some(si.toInt) }
catch { case nfe: NumberFormatException => None }
case _ => None
}
}
and use this instead. Now instead of performing a risky and implicit conversion from String to Int, when you perform a match it will all be handled properly, both the regex match (to avoid throwing and catching piles of exceptions on bad parses) and the exception handling even if the regex passes.
If you have something that has both a string and an int representation, create a new class and then have implicit conversions to each if you don't want your use of the object (which you know can safely be either) to keep repeating a method call that doesn't really provide any illumination.
I try not to convert anything implicitly just to convert it from one type to another, but only for the pimp my library pattern. It can be a bit confusing, when you pass a String to a function that takes an Int. Also there is a huge loss of type safety. If you would pass a string to a function that takes an Int by mistake the compiler could not detect it, as it assumes you want to do it. So always do type conversion explicitly and only use implicit conversions to extend classes.
edit:
To answer your updated question: For the sake of readability, please use the explicit getMillis. In my eyes valid use cases for implicits are "pimp my library", view/context bounds, type classes, manifests, builders... but not being too lazy to write an explicit call to a method.
In Scala it is possible formulate patterns based on the invididual characters of a string by treating it as a Seq[Char].
An example of this feature is mentioned in A Tour of Scala
This is the example code used there:
object RegExpTest1 extends Application {
def containsScala(x: String): Boolean = {
val z: Seq[Char] = x
z match {
case Seq('s','c','a','l','a', rest # _*) =>
println("rest is "+rest)
true
case Seq(_*) =>
false
}
}
}
The problem I have with this is the third line of the snippet:
val z: Seq[Char] = x
Why is this sort of cast necessary? Shouldn't a String behave like a Seq[Char] under all circumstances (which would include pattern matching)? However, without this conversion, the code snippet will not work.
There is some real abuse of terminology going on in the question and the comments. There is no cast in this code, and especially "So basically, this is a major concession to Java interoperability, sacrificing some type soundness" has no basis in reality.
A scala cast looks like this: x.asInstanceOf[Y].
What you see above is an assignment: val z: Seq[Char] = x
This assignment is legal because there is an implicit conversion from String to Seq[Char]. I emphasize again, this is not a cast. A cast is an arbitrary assertion which can fail at runtime. There is no way for the implicit conversion to fail.
The problem with depending on implicit conversions between types, and the answer to the original question, is that implicit conversions only take place if the original value doesn't type check. Since it's perfectly legal to match on a String, no conversion takes place, the match just fails.
Not 100% sure if this is correct, but my intuition says that without this explicit cast you would pattern match against java.lang.String, which is not what you want.
The explicit cast forces the Scala compiler to use Predef.stringWrapper implicit conversion; thus, as RichString extends Seq[Char], you are able to do a pattern match as if the string were a sequence of characters.
I'm going to echo everything that andri said. For interoperability, Scala strings are java.lang.Strings. In Predef, there's an implicit conversion from String to RichString, which implements Seq[Char].
A perhaps nicer way of coding the pattern match, without needing an intermediate val z to hold the Seq[Char]:
def containsScala(x: String): Boolean = {
(x: Seq[Char]) match {
...
}
}