Scala applying implicit functions to a collection - scala

EDIT: I'm using Scala 2.9.2
In Scala, I've defined a custom class which wraps a Double:
class DoubleWrap( d : Double ) {
def double( ) = d * 2
}
and an implicit conversion from Double to DoubleWrap:
implicit def wrapDouble( d : Double ) = new DoubleWrap( d )
This allows me to do the following:
scala> 2.5.double
res0: Double = 5.0
However, since there is an implicit conversion in Scala from Int to Double, I can also do the following:
scala> 2.double
res1: Double = 4.0
This operator can also be applied to all elements of a double-type collection using map
scala> List( 1.0, 2.0, 3.0 ).map( _.double )
res2: List[Double] = List(2.0, 4.0, 6.0)
However, if I attempt to apply the function to all elements of an integer collection, it doesn't work:
scala> List( 1, 2, 3 ).map( _.double )
<console>:10: error: value double is not a member of Int
List( 1, 2, 3 ).map( _.double )
Does anyone know why this is the case?

In scala, implicit conversions are not automatically chained. In other words, the compiler will look for a single implicit conversion that will allow the code to make sense, it will never try to apply two (or more) successive implicit conversions.
In your example, the fact that you can do 2.double has nothing to do with the fact that there is an implicit conversion from Double to Int in Predef.
As a proof, try this in the REPL:
scala> val i: Int = 2
i: Int = 2
scala> i.double
<console>:13: error: value double is not a member of Int
i.double
It does not compile.
So why does 2.double compile? Good question.
I thought I understood this intuitively: 2 can be interpreted as the Int value 2 or as the Double value 2.0 in the first place, so my intuition was that 2 is somehow already a Double in this context.
However, I think this is wrong, because even the following will compile, surpisingly: (2:Int).double (or even more strange: ((1+1):Int).double). I'll be honest, I am flabbergasted and have no idea why this compiles while val i: Int = 2; i.double does not.
So to sum up, it is normal that scala does not try to apply two implicit conversions at the same time, but for some reason this rule does not seem to apply to constant expressions.
And now for a way to fix your issue: simply modify your implicit conversion so that it accepts any type that is itself implicitly convertible to Double. In effect, this allows to chain the implicit conversions:
implicit def wrapDouble[T <% Double]( d : T ) = new DoubleWrap( d )

It's a bug which should soon be fixed.

Related

Scala Map's get vs apply operation: "type mismatch"

I am learning Scala and found the following:
List(('a', 1)).toMap get 'a' // Option[Int] = Some(1)
(List(('a', 1)).toMap) apply 'a' // Int = 1
(List(('a', 1)).toMap)('a') // Error: type mismatch;
found : Char('a')
required: <:<[(Char, Int),(?, ?)
(List(('a', 1)).toMap)('a')
But then assigning it to a variable works again.
val b = (List(('a', 1)).toMap)
b('a') // Int = 1
Why is this so?
The standard docs gives:
ms get k
The value associated with key k in map ms as an option, None if not found.
ms(k) (or, written out, ms apply k)
The value associated with key k in map ms, or exception if not found.
Why doesn't the third line work?
It's essentially just an idiosyncratic collision of implicit arguments with apply-syntactic sugar and strange parentheses-elimination behavior.
As explained here, the parentheses in
(List(('a', 1)).toMap)('a')
are discarded a bit too early, so that you end up with
List(('a', 1)).toMap('a')
so that the compiler attempts to interpret 'a' as an implicit evidence of (Char, Int) <:< (?, ?) for some unknown types ?, ?.
This here works (it's not useful, it's just to demonstrate what the compiler would usually expect at this position):
(List(('a', 1)).toMap(implicitly[(Char, Int) <:< (Char, Int)]))('a')
Assigning List(...).toMap to a variable also works:
({val l = List((1, 2)).toMap; l})(1)
Alternatively, you could force toMap to stop accepting arguments by feeding it to identity function that does nothing:
identity(List((1, 2)).toMap)(1)
But the easiest and clearest way to disambiguate implicit arguments and apply-syntactic sugar is to just write out .apply explicitly:
List((1, 2)).toMap.apply(1)
I think at this point it should be obvious why .get behaves differently, so I won't elaborate on that.
The signature is slightly different:
abstract def get(key: K): Option[V]
def apply(key: K): V
The issue is error handling: get will return None when an element is not found and apply will throw an exception:
scala> Map(1 -> 2).get(3)
res0: Option[Int] = None
scala> Map(1 -> 2).apply(3)
java.util.NoSuchElementException: key not found: 3
at scala.collection.immutable.Map$Map1.apply(Map.scala:111)
... 36 elided
Regarding the failing line: toMap has an implicit argument ev: A <:< (K,V) expressing a type constraint. When you call r.toMap('a') you are passing an explicit value for the implicit but it has the wrong type. Scala 2.13.0 has a companion object <:< that provides a reflexivity method (using the given type itself instead of a proper sub-type). Now the following works:
scala> List(('a', 1)).toMap(<:<.refl)('a')
res3: Int = 1
Remark: i could not invoke <:<.refl in Scala 2.12.7, the addition seems to be quite recent.

Why doesn't "1 Math.pow 2" work in scala?

In scala the following works
1 max 2
But the following don't
1 Math.pow 2
or
import Math.pow
1 pow 2
Can you explain why?
There are a couple of things going on here. In a nutshell:
Implicit casting of the constant "1" to an instance of Int
Leveraging of the "Space notation" for methods that take a single parameter
In the case of 1 max 2, the constant 1 is implicitly cast as an Int (i.e. an instance of class Int).
Since the Int class defines a method called "max" which takes a single parameter, you can use the space or infix notation. The following are all equivalent (run in spark-shell):
scala> 1 max 2
res8: Int = 2
scala> 1.max(2)
res9: Int = 2
scala> val x = 1 // Note the result type here is Int
x: Int = 1
scala> x.max(2)
res10: Int = 2
scala> x max (2)
res11: Int = 2
scala> 1 // Note the result type here is *also* Int
res12: Int = 1
scala> 1. // Here are all of the methods that are defined for an Int
!= + << >> byteValue ensuring formatted isInfinity isValidByte isWhole notify signum toChar toInt toString until
## - <= >>> ceil eq getClass isInstanceOf isValidChar longValue notifyAll synchronized toDegrees toLong unary_+ wait
% -> == ^ compare equals hashCode isNaN isValidInt max round to toDouble toOctalString unary_- |
& / > abs compareTo floatValue intValue isNegInfinity isValidLong min self toBinaryString toFloat toRadians unary_~ →
* < >= asInstanceOf doubleValue floor isInfinite isPosInfinity isValidShort ne shortValue toByte toHexString toShort underlying
Note that there are a whole bunch of methods available on an Int for example max, min, +, - and so on. Looking at the signature of say, +, we can see that + is a method that takes a single parameter. Therefore we can do the following:
scala> 1 + 2 // No surprises here
res15: Int = 3
scala> 1.+(2) // Huh? This works because + is a method of Int that takes a single parameter.
// This is effectively the same as your max example.
res16: Int = 3
scala> 1.+ // Here are the signatures of the + method.
def +(x: Char): Int
def +(x: Long): Long
def +(x: Float): Float
def +(x: Short): Int
def +(x: Double): Double
def +(x: Byte): Int
def +(x: String): String
def +(x: Int): Int
scala> 1 + 'A' // From the above, we can see that the following is also valid
res17: Int = 66 // because the return type is Int
scala> 1 + "41"
res18: String = 141 // In this case, the + operator is a String concatenation operator Because the return type is String
scala> 1 + "x"
res19: String = 1x // Also because the return is String, but possible more intuitive.
To the pow part of the question.
Math.pow is a method that takes 2 parameters. Because it takes 2 parameters the space notation is not available to you.
Also, pow is not a method that is associated with an Int. It is pretty much like a static method of the Math class (actually Math is an object). So, just like you can't say x.pow(y,z) you can not say 1.pow(y, z) on the other hand you can say Math.pow(x, 2) - to get x squared - because that matches the signature of the pow method.
Here is the signature of Math.pow:
scala> Math.pow
def pow(x$1: Double,x$2: Double): Double
This is a little less exciting than +, but it is clear that it takes 2 Doubles and returns a Double. The example of say Math.pow(2,2) works even though integers are supplied as parameters (when Doubles are required) because the Ints are automatically cast to Double.
I hope this helps explain what you are seeing. I encourage you to try these examples in spark-shell, sbt or some other scala REPL.
Methods doesn't the same signature:
override def max(that: Long): Long
def pow(x$1: Double,x$2: Double): Double
The first is a member of Int* and can be called on 1 whereas the second is called on Math object and must have two parameters.
* well actually of RichInt by implicit conversion, but to an intuitive use perspective, it comes to the same thing so this subtlety shouldn't bother you
Method:
override def max(that: Int): Int = math.max(self, that)
is from scala.runtime.RichInt class bc evry Int is wrapped by:
#inline implicit def intWrapper(x: Int) = new runtime.RichInt(x)
of class scala.LowPriorityImplicits
But RichInt doesn't have any pow method. And you have to respec Math.pow signature:
public static double pow(double a, double b)
and call it with 2 arguments or use your own developed wrapper, like:
object MainClass {
implicit class IntHelper(i:Int) {
def pow(p:Int): Double = Math.pow(i, p)
}
def main(args: Array[String]): Unit = {
println(1 pow 2)
}
}
output:
1.0
1 max 2 is just syntactic sugar for 1.max(2).
As 1 is an integer literal, all methods defined for scala.Int can be applied in this way.
Unfortunately pow is not a method of scala.Int
But you can find the related method in scala.math.BigDecimal and scala.math.BigInt.
So the following would work:
BigInt(1) pow 2
The max function you have used is from Int.
It's signature is:
def max(that: Int): Int
Since 1 is an Int, you can call 1 max 2 with infix notation. it is simply 1.max(2).
The pow function is from Math.
It's signature is
def pow(x: Double, y: Double): Double
Since 1 is not a Math, you can't simply call 1 pow 2. On the other hand in Int, there is no method such that def pow(that: Int): Int.
The one you can use is pow(a,2) which is the Math implementation.
Scala.Math Documentation says that pow function takes two arguments of type Double and returns the value of the first argument raised to the power of the second argument.
This is the working version of the pow function:
import Math.pow
val a: Double = 1
pow(a,2) // returns 1.0

Cast Numeric to Float in Scala

I have a generic function like the following:
def foo[N: Numeric](bar: N) = bar.asInstanceOf[Float]
When I call it with an Int type like so:
foo(1) //Fails, if it was a Float it would work fine though.
It fails at runtime. Why can't I cast like this and how do I make it work?
Why can't I cast like this
Because N is of type Int and not of type Float. An explicit cast will still not make these types align, you need to convert one type to another.
As #jwvh points out, it is possible to cast an Int to a Float and vise versa:
scala> (1.0).asInstanceOf[Int]
res14: Int = 1
scala> (1).asInstanceOf[Float]
res15: Float = 1.0
But a conversion from java.lang.Integer to scala.Float will fail:
scala> Integer.valueOf(1).asInstanceOf[Float]
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Float
at scala.runtime.BoxesRunTime.unboxToFloat(BoxesRunTime.java:109)
... 29 elided
Due to boxing/unboxing (Still, using the properties of Numeric for this task will be safer).
how do I make it work?
Numeric has a bunch of toX methods. You're looking for toFloat:
def foo[N: Numeric](bar: N) = implicitly[Numeric[N]].toFloat(bar)
Yields
scala> foo(1)
res10: Float = 1.0
Note implicitly is used here to summon the implicit Numeric[N] evidence you required with the context bound on N.

Scala add value to a char variable

I am creating a simple function in Scala
def addOne(m: Int): Int = m + 1
Using it with integers works normally, using double it throws a type mismatch error.
addOne(2) = 3
addOne(2.1) = error: type mismatch
When I use it with a character in double quotes, it throws a type mismatch as expected.
addOne("z") = error: type mismatch
However, when using a single quotes character it returns a value for that letter.
addOne('z') = 123
What is happening here and why is it like this?
The reason you can use a Char as an argument to a function taking an Int is because Scala performs an implicit conversion from Char to Int. This specific conversion is defined in the companion object of the Char class. See here:
http://www.scala-lang.org/api/2.12.1/scala/Char$.html (It seems like SO breaks this link at the $ character. Copy-paste it instead)
The function perfoming the conversion is called char2int. It converts the Char into its corresponding Unicode value as an Int.
When the Scala compiler sees that the types Char and Int don't match, it will first check if there are any available implicit conversions. It only gives a compile error if it didn't find any. If it finds an implicit conversion, it will insert that function call into your code. Your code is therefore transformed to this:
addOne(Char.char2int('z'))
If you want to make your own implicit conversion to, for example, let your function accept String, you can define this:
// Enable implicit conversions.
import scala.language.implicitConversions
// The "implicit" modifier is the important part here, not the name of the function.
implicit def string2int(s: String) = s.toInt
Now this compiles:
// This returns 6
addOne("5")
/*
* This throws a NumberFormatException due to my implementation of string2int.
* Create your own implementation of string2int if you want it to work properly.
*/
addOne("a")
Finally: Beware that implicit conversions are very powerful and therefore can be dangerous! See TheArchetypalPaul's comment for an explanation.
It is because addOne(m: Int) [The part after colon (:) ] tells Scala you will pass Int to it, not Double, not anything else.
if you want it to work for Double, try this, but then you will always get Double as Result.
def addone (m : Double ) = m+1
addone: (m: Double)Double
scala> addone(1)
res0: Double = 2.0
scala> addone(1.1)
res1: Double = 2.1
Scala map the char type using the ASCII Table. So, 'z' is mapped to 122, which is an integer. In the method, addOne('z'), the input parameter been cast to an integer (i.e. 122).
However, the input parameter in addOne(2.1) is 2.1, which is a double and in addOne("z") is a string. They cannot be cast to an integer automatically.
def addOne(m: Int): Int = m + 1 only accept an integer for m
It also work with a single quoted character (z) because it's translated into its ASCII value. The value for 'z' is 122 and you add 1.
scala> val foo: Int = 'z'
foo: Int = 122
scala> val bar = foo + 1
bar: Int = 123
If you want to make this working with double you can specify def addOne(m: Double): Double = m + 1

Scala: Option[T] as ?[T] (or even T?)

i tried
type ?[_] = Option[_]
def f(x: ?[Int]) = for (y <- x) yield y
(but i don't know what i am doing.)
insofar as types are just objects, i should be able to define a postix operator (i.e. zero arity method) for use in type signatures (i think). it might need a space like
def f(x: Int ?) = for (y <- x) yield y
scala makes it easy to use the Option type with matching and polymorphism, avoid null. but, most classes are (nullable) vars and java often returns vars. using classes and calling java are two of scala's selling points. an easy-to-write and easy-to-read syntax would support Options even more strongly.
what are all the things that scala does with "?" that make its parsing special.
ideally, one could write
def f(x: Int?) = for (y <- x) yield y
like other languages. can we do this in scala (without a macro)?
First, types are not objects. In fact, Scala has exactly two namespaces: values and types. They are very different things, and play by very different rules.
The postfix idea is kind of nice, actually, but it is not possible. There's an infix notation for types, though.
Now, to what you wrote:
type ?[_] = Option[_]
Each underscore has a different meaning. The underscore in ?[_] means ? is higher-kinded, but you don't care what it's type parameter is. The underscore in Option[_] means Option is an existential type. So when you write x: ?[Int], Scala will convert it to x: Option[t] { forSome type t }. This means that not only you don't get the Int, but the type parameter of Option is unknowable (you just know it exists).
However, it does compile:
scala> def f(x: ?[Int]) = for (y <- x) yield y
f: (x: ?[Int])Option[Any]
Which version of Scala did you use? 2.11? A co-worker of mine has already found some type inference regressions on 2.11, so that could be it.
The proper way to write the type alias would be this:
type ?[+A] = Option[A]
Not only we pass the type parameter along (it is a parameter, after all!), but we need to specify co-variance for it to act just Option (which is co-variant itself).
Now, as to your two questions, Scala has absolutely no special treatment of ?. And, no, you can't do this. This ? is not exactly widespread among languages either, and in all of them that support it, it is built in the language, and not something externally defined.
Besides, it's kind of a joke that, when interface with Java, typing out Option would be a problem -- not with the average identifier size in Java!
You intended to get an Option[Int] out:
scala> type ?[A] = Option[A]
defined type alias $qmark
scala> def f(x: ?[Int]) = for (y <- x) yield y + 1
f: (x: ?[Int])Option[Int]
and it does compile anyway.
You could maybe
scala> type ?[A,_] = Option[A]
defined type alias $qmark
scala> def f(x: Int ? _) = for (y <- x) yield y + 1
f: (x: ?[Int, _])Option[Int]
or similar.
scala> def f(x: Int ?_) = for (y <- x) yield y + 1
f: (x: ?[Int, _])Option[Int]
looks more postfixy.
P.S. Still curious whether variance annotation on type alias is required or merely advisable.
scala> type ?[A] = Option[A]
defined type alias $qmark
scala> trait X ; trait Y extends X ; trait Z extends X
defined trait X
defined trait Y
defined trait Z
scala> val x: ?[X] = null.asInstanceOf[?[Y]] // why is this OK?
x: ?[X] = null
scala> class C[A]
defined class C
scala> val c: C[X] = null.asInstanceOf[C[Y]] // like this is not OK
<console>:10: error: type mismatch;
found : C[Y]
required: C[X]
Note: Y <: X, but class C is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
val c: C[X] = null.asInstanceOf[C[Y]]
^
Maybe compare SI-8522 and related issues.
You might consider a renaming import. When you create a type alias you only rename a type. When you rename a symbol during import you include all referents of that name, both type and value.
To wit:
scala> import scala.{Option => ?}
import scala.{Option=>$qmark}
scala> val oi1: ?[Int] = Some(1)
oi1: Option[Int] = Some(1)
scala> def mi1(oi: ?[Int]): Int = oi.getOrElse(-1)
mi1: (oi: Option[Int])Int
scala> mi1(None)
res1: Int = -1
scala> mi1(?(1))
res2: Int = 1
Compare with this:
scala> type ?[A] = Option[A]
scala> def mi1(oi: ?[Int]): Int = oi.getOrElse(-1)
mi1: (oi: ?[Int])Int
scala> mi1(?(1))
<console>:10: error: not found: value ?
mi1(?(1))
^