Is Array[String] not a subclass of Seq[String] in Scala? - arrays

I wrote a method that accepts objects of all subclasses of Seq[String]. Unfortunately it won't accept an object of the type Array[String]. Is Array[String] not a subclass of Seq[String]?
scala> def test[T <: Seq[String]](x: T) = {}
test: [T <: Seq[String]](x: T)Unit
scala> val data = "This is a test string"
data: java.lang.String = This is a test string
scala> test(data.split(" "))
<console>:10: error: inferred type arguments [Array[java.lang.String]] do not conform to method test's type parameter bounds [T <: Seq[String]]
test(data.split(" "))

No, Array[String] translates to regular JVM arrays, like the ones you see in Java: String[].
The reason why you see all the operations on Array[String] that you see on other Scala Seq collections is that there is an implicit conversion from Array[T] to ArrayOps[T].
Do this:
def test[T <% Seq[String]](x: T) = {}
This is called a view bound. It means that T should either be a subtype of Seq[String] or there should exist an implicit conversion in scope which converts T into a Seq[String]. Behind the scenes, the compiler actually adds an implicit parameter to test, so this method becomes:
scala> def test[T <% Seq[String]](x: T) = {}
test: [T](x: T)(implicit evidence$1: T => Seq[String])Unit
This implicit evidence$1 is the function which now acts as the implicit conversion from T to Seq[String] within the body of the method.

The sources (or the API docs) state, that Array is defined as
final class Array[T] extends Serializable with Cloneable
That is, it is not a subtype of Seq. However, the docs also mention an implicit conversion WrappedArray, where the latter is a subset of Seq.

Related

Type Mismatch in scala, Array[Int] and Array[Option[Int]]

First, I am new to Scala, So apologies if the following question is too simple.
I have written the following code to the find the values of the keys that I supply in an array from the map.
def stringToCountMap(inputArray: Array[String], inputMap:Map[String,Int]) : Array[Int] = {
return inputArray.map(x => inputMap.get(x))
}
I got the following error,
type mismatch;
found : Array[Option[Int]]
required: Array[Int]
return inputArray.map(x => inputMap.get(x))
Question:
1) Can anyone explain what is Option[Int]?
2) What is my mistake here ?
Thanks in advance.
Option is Scala's option type (also called a nullable type). It represents a case where the value may not exist.
Consider a map that doesn't contain a requested key. How would you handle a request for the key? One option is to result in an error, such as by throwing an exception. Another is to return a special value that indicates there is no value. Map.get does the latter, using Option as the special type and None as the value. This means the return type of Map.get isn't the value type of the map (Int), but Option applied to the value type (Option[Int]).
To correct the type declaration, change the return type:
def stringToCountMap(inputArray: Array[String], inputMap:Map[String,Int]) : Array[Option[Int]] = {
inputArray.map(x => inputMap.get(x))
}
You can leave out the return type of stringToCountMap and let type inference handle it:
def stringToCountMap(inputArray: Array[String], inputMap:Map[String,Int]) = {
inputArray.map(x => inputMap.get(x))
}
As a consequence, missing keys from the input map get carried through:
scala> stringToCountMap(Array("a", "def"), Map("a" -> 1, "bc" -> 2))
res0: Array[Option[Int]] = Array(Some(1), None)
Option[T] is a wrapper around a value of type T. Its purpose is to prevent NullPointerException, that you might know from Java. A value of type Option[T] might either be None, which, as the name implies is an object that represents nothing, or it might be Some(x: T), which represents an existing value.
inputMap(x) returns an Option[Int], since you have no guarantee that the x key exists in inputMap. If it does, it returns Some(value: Int), else it returns None.
Calling stringToCountMap(Array("a", "b", "c"), Map("a" -> 1, "c" -> 2)) results in Array(Some(1), None, Some(2))
If you want an Array[Int] instead, you might do something like inputArray.map(x => inputMap.getOrElse(x, 0)).get. The getOrElse method has two parameters, where the first one is the key, and the second one is the default value. inputArray.map(x => inputMap.get(x).getOrElse(0)) has the same effect, since calling getOrElse(value) on an Option either unwraps the Some object, or returns the default value.
Now, stringToCountMap(Array("a", "b", "c"), Map("a" -> 1, "c" -> 2)) results in Array(1, 0, 2).
You might also want to omit the keys missing in the input array. In that case, you might do inputArray.flatMap(x => inputMap.get(x)). flatMap is a function similar to map, but it returns strictly, as the name implies, flat collections. For example, calling flatMap(x => x) on an Array[Array[Int]] would return an Array of all the values in the 2D array in a single row.
Here, Option is a collection, as well. If it is of type Some, it contains a single value, if it is None, it is an empty collection. Thus, in the resulting array you would only have the values of the keys present in the map, and the keys not present in the map are skipped.
Now, stringToCountMap(Array("a", "b", "c"), Map("a" -> 1, "c" -> 2)) results in Array(1, 2).
First, there is no need to use 'return' in scala to return any value.
def stringToCountMap(inputArray: Array[String], inputMap:Map[String,Int]) = {
inputArray.map(x => inputMap.get(x))
}
When you get the value of key from Map, It returns result in Option.
For example:
scala> val map = Map(1-> "a",2 -> "b")
map: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b)
scala> map.get(1)
res0: Option[String] = Some(a)
scala> map.get(3)
res1: Option[String] = None
When you try to get the value of key, which does not exist. In java, you have encountered with NullPointerException. So when there is no value, it returns None to avoid exception.
For more info refer
In your method, you have given return type as Array[Int] but function returns Array[Option[Int]] that's why it throws compilation error.

Why is there no Functor instance for Array in Scalaz

It looks like scalaz provides a Functor instance for List but does not provide it for Array (or Seq).
scala> val fa = Functor[Array]
<console>:17: error: could not find implicit value for parameter F: scalaz.Functor[Array]
val fa = Functor[Array]
^
scala> val fl = Functor[List]
fl: scalaz.Functor[List] = scalaz.std.ListInstances$$anon$1#20c4b59
scala> val fl = Functor[Seq]
<console>:17: error: could not find implicit value for parameter F: scalaz.Functor[Seq]
val fl = Functor[Seq]
^
Why is that ? Aren't they functors ?
Scalaz requires that objects follow the laws for Functors. It also prescribes to the "everything immutable" philosophy of code construction. That said, Array is mutable, so they wouldn't create a Functor instance for it. Seq on the other hand is an abstract interface and it is unknown what the "correct" data type will be. That is, for Seq how to know which underlying object to return and therefore not violate any laws?

Function defined with generic types

I searched for a while and I was not able to find a clear and satisfying answer to this question.
Suppose I have this little function to compute the mean value of an Array:
def meanofArray(s:Array[Double]) : Double = s.sum/s.length
the problem is that is Array has to be Double.
If for example I have this Array:
val x = Array( 1 , 2 , 3 , 4 )
and make the call :
println(meanofArray(x))
I get the error found : Array[Int]
required: Array[Double]
How can I implement meanofArray so as to be able to accept any type of Int , Long , Float and Double with no errors ?
My thought was Generic types :
def meanofArray(s:Array[T]) : Double = s.sum/s.length
but I could not get it working.
Any other idea is welcomed !
The issue you are running into demonstrates the beauty of type abstraction.
Based solely on the signature of the function you provided
def meanofArray(s:Array[T]) : T
we know quite a bit about what this function can and can not do, as long as it does not do shady things like reflection of course. It can not do things like sort the Array, find the sum, etc. Based on some unfortunate characteristics of Scala, there are some things which we can not be sure it does not do, like use equals or toString for some T.
You need to declare some additional information about the type T in the function signature.
def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double
Here we are declaring that T must have a definition of Numeric[T] defined and in scope at call sites. Numeric[T] provides the definitions of functions required in order to do the calculations you need. We are now declaring that this function may do more things like sort the array or find the sum.
And the implementation you are looking for...
def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double =
s.view.map(ev.toDouble).sum / s.length
EDIT: Map s to view of Array[Double] to decrease likelihood of overflow. Taken from #Eastsun's answer. Taking Numeric[T] implicitly is strongly preferred over implicit view from T => Double in my opinion though.
Here is what you want(The parameter's type of mean is Seq[A] so that you can use it as mean(List(1, 2, 3)) or mean("ABCDEFG")):
scala> def mean[A](arr: Seq[A])(implicit view: A => Double): Double =
(arr map view sum) / arr.size
mean: [A](arr: Seq[A])(implicit view: A => Double)Double
scala> mean(Array(1, 2, 3, 4))
res0: Double = 2.5
Compare with #drstevens 's solution:
scala> def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double =
| ev.toDouble(s.sum) / s.length
meanofArray: [T](s: Array[T])(implicit ev: scala.math.Numeric[T])Double
scala> meanofArray(Array[Byte](111, 111, 111, 111))
res3: Double = -17.0 //Wrong!!!
scala> mean((Array[Byte](111, 111, 111, 111)))
res4: Double = 111.0 //Right !!!
This is the cleanest solution i could come up for your question.
def mean[A <% Double : Numeric](seq:Seq[A]) = seq.sum/seq.length
Here, Seq.sum needs an evidence of A conforms to be a Numeric type. (: in the type parameter)
And that division to work, the result of seq.sum should be viewable as a Double. Altogether this results a result mean of Double type

Scala case class arguments instantiation from array

Consider a case class with a possibly large number of members; to illustrate the case assume two arguments, as in
case class C(s1: String, s2: String)
and therefore assume an array with size of at least that many arguments,
val a = Array("a1", "a2")
Then
scala> C(a(0), a(1))
res9: C = c(a1,a2)
However, is there an approach to case class instantiation where there is no need to refer to each element in the array for any (possibly large) number of predefined class members ?
No, you can't. You cannot guarantee your array size is at least the number of members of your case class.
You can use tuples though.
Suppose you have a mentioned case class and a tuple that looks like this:
val t = ("a1", "a2")
Then you can do:
c.tupled(t)
Having gathered bits and pieces from the other answers, a solution that uses Shapeless 2.0.0 is thus as follows,
import shapeless._
import HList._
import syntax.std.traversable._
val a = List("a1", 2) // List[Any]
val aa = a.toHList[String::Int::HNil]
val aaa = aa.get.tupled // (String, Int)
Then we can instantiate a given case class with
case class C(val s1: String, val i2: Int)
val ins = C.tupled(aaa)
and so
scala> ins.s1
res10: String = a1
scala> ins.i2
res11: Int = 2
The type signature of toHList is known at compile time as much as the case class members types to be instantiate onto.
To convert a Seq to a tuple see this answer: https://stackoverflow.com/a/14727987/2483228
Once you have a tuple serejja's answer will get you to a c.
Note that convention would have us spell c with a capital C.

incredible implicit Array conversion in scala

According to Scaladoc, there is no method named map in Array class, but there is an implicit function implicit def intArrayOps (xs: Array[Int]): ArrayOps[Int] defined in scala.Predef. So you can apply map on Array(1,2,3,4) if you like. But What I am confused about is that the map result is of type Array[Int], not ArrayOps[Int]. Here is my test:
scala> val array = Array(1,2,3,4)
array: Array[Int] = Array(1, 2, 3, 4)
scala> array.map(x => x)
res18: Array[Int] = Array(1, 2, 3, 4)
scala> res18.isInstanceOf[Array[Int]]
res19: Boolean = true
scala> res18.isInstanceOf[scala.collection.mutable.ArrayOps[Int]]
warning: there wre 1 unchecked warnings; re-run with -unchecked for details
res20: Boolean = false
It indeed returns an array, as intended and as is convenient, there is no reason you would need an ArrayOps, it is intended only to provide extra methods to arrays. The doc is wrong.
The routine is actually not implemented in ArrayOps. As most collection methods, it is inherited from TraversableLike. And you see two map methods in the doc:
def map [B] (f: (T) ⇒ B): ArrayOps[B]
def map [B, That] (f: (T) ⇒ B)(implicit bf: CanBuildFrom[Array[T], B, That]): That
Only the second one exists (inherited from TraversableLike). It is intended to allow implementation of map in just one place (traversable like) while allways giving the best possible behavior. For instance, a String is a of Seq[Char], if you map with a function from character to character, you get a String, but if you map from collection to say Int, the result cannot be a String and it will just be a Seq. This is explained in much detail in the paper fighting the bit rot with types.
However, this makes for a very complex signature, which does not reflect the simplicity of using the method, and makes very poor documentation most of the time (you normally would have to chase to which CanBuildFrom in implicit scope would work). This was discussed in this most famous scala question of stack overflow. So the tool scaladoc was extended so that a simpler entry, corresponding to intended usage, may appear. If you look at the source of GenTraversableLike, where the routine is introduced, you will see the following in the scaladoc for map (and a similar one in many methods)
#usecase def map[B](f: A => B): $Coll[B]
Subtypes add in their doc #define Coll <className>, and map (among others) appears with the simplified signature, marked [Use case]. In the source of ArrayOps, there is a #define Coll ArrayOps where it should be Array.
You can use the REPL with the -Xprint:typer option to see what's going on. Here is the output of the map method, reformatted for easier reading:
$ scala -Xprint:typer
scala> Array(1,2,3,4).map(x => x)
[[syntax trees at end of typer]]// Scala source: <console>
// some lines deleted
private[this] val res0: Array[Int] =
scala.this.Predef.intArrayOps(scala.Array.apply(1, 2, 3, 4))
.map[Int, Array[Int]]
(( (x: Int) => x ))
(scala.this.Array.canBuildFrom[Int](reflect.this.Manifest.Int));
So simplifying for package names here is what happens:
intArrayOps(Array(1,2,3,4)) // converts to ArrayOps
.map[Int, Array[Int]] // calls map with parameter lists below
((x:Int) => x) // pass identity function as fisrt param
(Array.canBuildFrom[Int]// pass builder for Array[Int] as second param
(Manifest.Int)) // pass class manifest for Int
So there is indeed a conversion to ArrayOps (first line). It returns ArrayOps[Int].
The ArrayOps.map[Int, Array[Int]] method is then called on it. Then as didierd explain, the original signature for map - not the simplified signature - indicates that the return type inferred will be Array[Int]

Resources