How to unimport String "+" operator in Scala? - scala

I'm writing a DSL where the "+" operator is strictly numeric, like some other popular languages. It's close, but the String "+" operator is messing up my implicit conversions. What's the syntax for unimporting an operator of the String class?
Just to be clearer, instead of this:
scala> var x = "2" + 3;
x: java.lang.String = 23
I'd like to get x: Int = 5
I imagine I just need 2 things to make that happen:
Remove (unimport within my scope) the definition of "+" from Strings
Define an implicit conversion of String to Int
I'm stuck on the first step.
Thanks

According to section 12.3.1 of the Scala spec, the + method for String has special treatment by the compiler. I don't know for sure, but I think this means you can't "unimport" it, which is a shame because it really breaks the type system (much like the related toString method).
Could you use a different name for the operator in your DSL, eg, ++ or &?

The + method for a string is a method on of the String class (and therefore on each string object), and as such it cannot be unimported.

You can't unimport it, but you could use +: and define it on the int class. What would be best is if you write it like this: "2".toInt + 3.

Related

Why can't I call the Scala Seq.empty[Int]() method?

I'm trying to create a class with an attribute named _1d_matrix, which is a 1D matrix. I would want to fill it with 0.
Thus, I tried to call the method empty of the Seq object (of course not the trait), as mentionned in the documentation : http://www.scala-lang.org/api/2.12.3/scala/collection/Seq$.html#empty[A]:CC[A]
For the moment my code is :
class Calc(val width : Int) {
private val _1d_matrix : Seq.empty[Int]()
}
But there are three errors :
empty is undefined
() can't be written (2 errors : "; or newline is expected" + "definition or declaration is expected")
I think it's because it's not allowed to directly make use of Seq instead of List (e.g.). But : why ? After all, Seq is an object (and a trait but not in my code).
Right way to initialize field looks like this:
private val _1d_matrix = Seq.empty[Int]
or
private val _1d_matrix: Seq[Int] = Seq()
There are two ways to define a 0-arity (no arguments) method in scala - with a pair of parenthesis after the name def exit(), and without one: def empty.
When it is defined in the former way, you can call it with or without parenthesis: exit() or exit - both work. But when parenthesis are not included into the method definition, you can not have them at the call site either: Seq.empty[Int] works, but Seq.empty[Int]() does not.
The convention is to use parenthesis for functions that have side effects, and not use them for purely functional calls.
The other problem with your code is that you have a : where it needs to be an assignment.
So, something like this should work:
val _1d_matrix = Seq.empty[Int]
Your thinking is correct but you have a couple syntax errors.
private val _1d_matrix : Seq.empty[Int]()
: here is used to define a type annotation, so it's trying to find the type Seq.empty rather than the method. That fails because there is no such type.
Use = instead to assign a value. Adding the type here is optional since Scala is able to infer the correct type.
The other is that the empty method is defined without parens, so you must use Seq.empty[Int] instead of Seq.empty[Int]()

Why can I concatenate String and Int in Scala?

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"))

Scala String Interpolation

I am new to Scala and have read threads related to String Interpolation.
My requirement is that , I want to find the type of an expression, before actually evaluating the expression.
To make it clear:
val tesType = s"${{10 * Math.cos(0)+ 3}.getClass}"
This gives me the return type of the entire expression.
Is it possible to generalise this by replacing the actual expression by a variable containing the expression?
Something like:
val expression="10 * Math.cos(0)+ 3"
val tesType = s"${{expression}.getClass}"
Would something like this be possible or I am totally wrong in thinking in this direction?
Thanks
It's not possible to do with string interpolation. What you actually want to do is to compile scala code in runtime from string (file etc).
For ex twitter Eval library can be used for this purposes:
https://eknet.org/main/dev/runtimecompilescala.html
If you want the expression to be in a string, then see mst's answer (you can also use scala-compiler itself as a library, but its API is harder to use).
If you have an expression as an expression, you can do
import scala.reflect.runtime.universe._
def typeOfExpr[T: TypeTag](t: => T) = typeOf[T]
typeOfExpr(10 * Math.cos(0)+ 3) // returns Double

Implicit conversion paradox

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.

How Can I eliminate this dot? I was testing Implicit Conversion

I have been trying to use the Int type like this: 10 twotimes.
It is similar to Akka's duration package which allows to write for example 10 minutes and 5 seconds.
I did the following:
object ImplicitConversion2 {
class Test(val a:Int) {
def threetimes = a * 3
}
implicit def IntToTest(e:Int) = new Test(e)
5.threetimes //> res0: Int = 15
10.threetimes //> res1: Int = 30
11.threetimes //> res2: Int = 33
}
In this case, I can use threetimes like 10.threetimes, that is with a dot between 10 and threetimes. I want to eliminate the dot between the Int and threetimes.
But if I write 5 threetimes, an error occurs.
How can I do that?
Which version of Scala are you referring to? With version 2.10.1 I get a feature warning, namely that I should add the following
import scala.language.implicitConversions
to activate implicit conversions. After doing so, as you said, 10.threetimes works fine, but for 10 threetimes I get another feature warning which tells me to add:
import scala.language.postfixOps
to enable postfix operators. After doing so, everything works fine.
EDIT: Here is an excerpt from the original SIP (to be found here) which gives the reason why postfix operators are now discouraged by default (i.e., you will get a warning if you use them; in later versions this might turn into an error instead of a warning):
postfixOps. Only where enabled, postfix operator notation (expr op) will be allowed. Why keep the feature? Several DSLs written in Scala need the notation. Why control it? Postfix operators interact poorly with semicolon inference. Most programmers avoid them for this reason.
As Luigi Plinge correctly states in his comment, the compiler tries to use the line after a call to a postfix operator as argument. If you look at the error message it becomes clear:
<console>:10: error: Int does not take parameters
3 threetimes
This error means, that it tries to use something as an argument, where it doesn't belong. The fact, that using dot-notation fixes this error should make it obvious, what goes wrong. The only way to safely use postfix operators is to pass them as arguments, where either the ) or the , terminates the expression.
def foo(x: Int, y: Int) = x + y
foo(4 threetimes, 8 threetimes)