views:

95

answers:

3

Let's say I have a function like this (it's only an example, so do not offer me better ways to create 0,1,2,... style array):

def createArray(size: Int): Array[Int] = {
  for (i <- 0 until size) yield i
}

But the compiler get upset with some mysterious type mismatch error:

(fragment of compare-images.scala):39: error: type mismatch;
 found   : RandomAccessSeq.Projection[Int]
 required: Array[Int]
      for (i <- 0 until size) yield i
            ^
one error found
!!!
discarding <script preamble>

I'm sure, the reason has something to do with the fact that until method's return type is Range, not Array. Yet, why the compiler can't just cast the types? Interestingly the problem goes away when I remove the return type of my function:

def createArray(size: Int) = {
  for (i <- 0 until size) yield i
}

But I want my function to return Array...

I also have another function, which goes like this:

def screateArray2: Array[Int] = {
  val a = Array(1,2,3,4,5)
  for (i <- a) yield i
}

It compiles without any problems. It yields values very similarly to the first example, but doesn't use until method...

Am I missing something about Scala's type system?

I'm quite new to Scala.

EDIT: I sort of solved my problem like this:

def crop(data: Array[Int]): Array[Int] = (
  for (i <- 0 until data.size) yield i
).toArray

But in my view it's anything but readable...

+5  A: 

You can't cast the types just because an Array is not a Range, nor a superclass of it. I think their most common supertype would be IndexedSeq. So your example would be similar to a method which you declare to return an Int, while the implementation would return a String, for instance. Range does have a toArray method, however, so if you want to use the until style and return an Array, you can do it like this:


scala> (for (i <- 0 until 5) yield i).toArray
res0: Array[Int] = Array(0, 1, 2, 3, 4)

or, if do don't like this, there's another way, but that doesn't use the until style:


scala> for (i <- Array.range(0,5)) yield i          
res1: Array[Int] = Array(0, 1, 2, 3, 4)

Also, check this related question for returning custom types from a for comprehension

Arjan Blokzijl
I should have said "infer" (or whatever Scala compiler does to match types) instead of "cast".
Vilius Normantas
But the types do not match, since a Range is not a subtype of Array, so there's no question of inferring the right result type here. You have to do a conversion to get that.
Arjan Blokzijl
Yeah, but you really do not have to be a genius compiler to convert Range to Array automatically instead of bothering me with type mismatch errors :)
Vilius Normantas
I think you kind of have to be a compiler genius to auto-coerce between arbitrary types.
Adam Rabung
@Vilius Mind you, the compiler isn't bothering you. It is saying that perhaps you wrote the wrong code, as you are generating one type of data and returning another.
Daniel
+3  A: 

So, How about this one:

scala> import scala.collection.breakOut
import scala.collection.breakOut

scala> def createSet(size: Int): Set[Int] = (
     |     for(i <- 0 until size) yield i
     | )(breakOut)
createSet: (size: Int)Set[Int]

scala> def createList(size: Int): List[Int] = (
     |     for(i <- 0 until size) yield i
     | )(breakOut)
createList: (size: Int)List[Int]

scala> def createArray(size: Int): Array[Int] = (
     |     for(i <- 0 until size) yield i
     | )(breakOut)
createArray: (size: Int)Array[Int]

scala> createArray(10)
res3: Array[Int] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

scala> createList(5)
res4: List[Int] = List(0, 1, 2, 3, 4)

scala> createSet(4)
res5: Set[Int] = Set(0, 1, 2, 3)

scala>
Eastsun
What exactly the `(breakOut)` part does? And I was complaining that my `.toArray` solution was unreadable :)
Vilius Normantas
@Vilius Yes, that isn't particularly readable, and I didn't even think it was possible. Look up questions about Scala's for-comprehension, and about `breakOut`, as this is explained elsewhere.
Daniel
+1  A: 

Arjan's solution can be shortened to:

 def createArray(size: Int) = (0 until size).toArray

[Edit] Of course you can remove values you don't need before you create the array

 def createArray(size: Int) = (0 until size).filter(LOTSOFINTERESTINGSTUFF).toArray
Landei
My actual method is more like `for (i <- 0 until size; if (LOTS OF STUFF HERE)) yield i`. I just simplified it for sake of example.
Vilius Normantas
See my [Edit]...
Landei
Actually, `withFilter` would be better than `filter` here.
Daniel