tags:

views:

156

answers:

5

for instance in Clojure:

user=> (map #(* % 2) (concat [1.1 3] [5 7]))

(2.2 6 10 14)

but in Scala:

scala> List(1.1,3) ::: List(5, 7) map (_ * 2)

<console>:6: error: value * is not a member of AnyVal
       List(1.1,3) ::: List(5, 7) map (_ * 2)
                                       ^

Here ::: obtain a list of type List,oops then failed. Can any coding more intuitively like Clojure above?

+4  A: 

Here are you individual lists:

scala> List(1.1,3)
res1: List[Double] = List(1.1, 3.0)

scala> List(5, 7)
res2: List[Int] = List(5, 7)

The computed least upper bound (LUB) of Double and Int, needed to capture the type of the new list that includes elements of both the arguments lists passed to :::, is AnyVal. AnyVal includes Boolean, e.g., so there are no numeric operations defined.

Randall Schulz
+2  A: 

As Randall has already said, the common supertype of Double and Int is AnyVal, which is inferred in this case. The only way I could make your example work is to add a type parameter to the second list:


scala> List[Double](1.1,3) ::: List(5, 7) map (_ * 2)
:6: error: value * is not a member of AnyVal
       List[Double](1.1,3) ::: List(5, 7) map (_ * 2)

scala> List(1.1,3) ::: List[Double](5, 7) map (_ * 2)
res: List[Double] = List(2.2, 6.0, 10.0, 14.0)

I guess that in the latter case the implicit conversion from Int to Double is applied. I'm not sure why it this not applied in when adding the type parameter to the first list, however.

Arjan Blokzijl
+1  A: 

The first list is of type List[Double] because of literal type widening. Scala sees the literals, and notes that, even though they are of different types, they can be unified by widening some of them. If there was no type widening, then the most common superclass, AnyVal would be adopted.

List(1.1 /* Double literal */), 3 /* Int literal */)

The second list is clearly List[Int], though explicit calling for Double will result in type widening for the literals.

List(5 /* Int literal */, 7 /* Int literal */)

Now, it's important to note that type widening is something that happens at compile time. The compiled code will not contain any Int 3, only Double 3.0. Once a list has been creater, however, it is not possible to do type widening, because the stored objects are, in fact, different.

So, once you concat the two lists, the resulting type will be a superclass of Double and Int. Namely, AnyVal. As a result of Java interoperability, however, AnyVal cannot contain any useful methods (such as numeric operators).

I do wonder what Clojure does internally. Does it convert integers into doubles when concatenating? Or does it store everything as Object (like Scala does) but has smarter math operators?

Daniel
A: 

I see two ways to make it work - braces for the second part

scala> List (1.1, 3) ::: (List (5, 7) map (_ * 2))
res6: List[AnyVal] = List(1.1, 3, 10, 14)

or explicit Doubles everywhere:

scala> List (1.1, 3.) ::: List (5., 7.) map (_ * 2)  
res9: List[Double] = List(2.2, 6.0, 10.0, 14.0)

which is of course semantically different.

user unknown
A: 

Why not just,

(List(1.1,3) ::: List(5, 7)).asInstanceOf[List[Double]] map (_ * 2)

?

missingfaktor