Why are Arrays invariant, but Lists covariant? - arrays

E.g. why does
val list:List[Any] = List[Int](1,2,3)
work, but
val arr:Array[Any] = Array[Int](1,2,3)
fails (because arrays are invariant). What is the desired effect behind this design decision?

Because it would break type-safety otherwise.
If not, you would be able to do something like this:
val arr:Array[Int] = Array[Int](1,2,3)
val arr2:Array[Any] = arr
arr2(0) = 2.54
and the compiler can't catch it.
On the other hand, lists are immutable, so you can't add something that is not Int

This is because lists are immutable and arrays are mutable.

The normal answer to give is that mutability combined with covariance would break type safety. For collections, this can be taken as a fundamental truth. But the theory actually applies to any generic type, not just collections like List and Array, and we don't have to try and reason about mutability at all.
The real answer has to do with the way function types interact with subtyping. The short story is if a type parameter is used as a return type, it is covariant. On the other hand, if a type parameter is used as an argument type, it is contravariant. If it is used both as a return type and as an argument type, it is invariant.
Let's look at the documentation for Array[T]. The two obvious methods to look at are for the ones for lookup and update:
def apply(i: Int): T
def update(i: Int, x: T): Unit
In the first method T is a return type, while in the second T is an argument type. The rules of variance dictate that T must therefore be invariant.
We can compare the documentation for List[A] to see why it is covariant. Confusingly, we would find these methods, which are analogous to the methods for Array[T]:
def apply(n: Int): A
def ::(x: A): List[A]
Since A is used as both a return type and as an argument type, we would expect A to be invariant just like T is for Array[T]. However, unlike with Array[T], the documentation is lying to us about the type of ::. The lie is good enough for most calls to this method, but isn't good enough to decide the variance of A. If we expand the documentation for this method and click on "Full Signature", we are shown the truth:
def ::[B >: A](x: B): List[B]
So A does not actually appear as an argument type. Instead, B (which can be any supertype of A) is the argument type. This does not place any restriction on A, so it really can be covariant. Any method on List[A] which has A as an argument type is a similar lie (we can tell because these methods are marked as [use case]).

The difference is that Lists are immutable while Arrays are mutable.
To understand why mutability determines variance, consider making a mutable version of List - let's call it MutableList. We'll also make use of some example types: a base class Animal and 2 subclasses named Cat and Dog.
trait Animal {
def makeSound: String
}
class Cat extends Animal {
def makeSound = "meow"
def jump = // ...
}
class Dog extends Animal {
def makeSound = "bark"
}
Notice that Cat has one more method (jump) than Dog.
Then, define a function that accepts a mutable list of animals and modifies the list:
def mindlessFunc(xs: MutableList[Animal]) = {
xs += new Dog()
}
Now, horrible things will happen if you pass a list of cats into the function:
val cats = MutableList[Cat](cat1, cat2)
val horror = mindlessFunc(cats)
If we were using a careless programming language, this will be ignored during compilation. Nevertheless, our world will not collapse if we only access the list of cats using the following code:
cats.foreach(c => c.makeSound)
But if we do this:
cats.foreach(c => c.jump)
A runtime error will occur. With Scala, writing such code is prevented, because the compiler will complain.

Related

Is the return type of this method a slice or a borrowed array?

I am working through the Rust book, specifically the "Departments" exercise in section 8.3.
I've become a little confused by what is being returned from a method I've created.
#[derive(Debug, PartialEq, Eq, Hash)]
enum Department {
Sales,
Engineering,
}
#[derive(Debug)]
struct Company {
users: HashMap<Department, Vec<String>>,
}
impl Company {
fn add_user_to_department(&mut self, department: Department, employee: String) {
let users = self.users.entry(department).or_insert(Vec::new());
users.push(employee);
}
fn get_users_in_department(&self, department: Department) -> &[String] {
match self.users.get(&department) {
Some(u) => &u,
None => &[]
}
}
}
At first I thought the method get_users_in_department is returning a slice of the Vec (or in the case of None a static literal).
But in my match statement I am not using the slice syntax, I am returning a borrowed reference to the Vec.
match self.users.get(&department) {
Some(u) => &u,
None => &[]
}
If I was returning a slice wouldn't I have to return Some(u) => &u[..],
I guess my confusion boils down to what the return type of get_users_in_department is. Is it returning a reference to an array of String, or a slice of an array of String. Are these two types interchangeable due to some sort of coercion? If it's possible to differentiate between the two, how and when would I want to return one or the other?
From Type Coercions in The Rust Reference:
Type coercions are implicit operations that change the type of a value. They happen automatically at specific locations and are highly restricted in what types actually coerce.
Coercion is allowed between the following types:
&T or &mut T to &U if T implements Deref<Target = U>.
This kind of type coercion is called a deref coercion. Relevant example: Vec<T> implements Deref<Target = [T]> so &Vec<T> can be coerced into &[T].
A coercion can only occur at certain coercion sites in a program; these are typically places where the desired type is explicit or can be derived by propagation from explicit types (without type inference). Possible coercion sites are:
Function results—either the final line of a block if it is not semicolon-terminated or any expression in a return statement.
Example:
// no type coercion
fn vec_to_vec<T>(vec: &Vec<T>) -> &Vec<T> {
vec
}
// &Vec<T> coerced to &[T] via deref coercion
fn vec_to_slice<T>(vec: &Vec<T>) -> &[T] {
vec
}
playground
To answer your question more directly, the type of &u inside get_users_in_department is &Vec<String> but it's being coerced into &[String] by the compiler for you.

What expressions are allowed as the array length N in [_; N]?

Please consider the following minimal example in Rust:
const FOOBAR: usize = 3;
trait Foo {
const BAR: usize;
}
struct Fubar();
impl Foo for Fubar {
const BAR: usize = 3;
}
struct Baz<T>(T);
trait Qux {
fn print_bar();
}
impl<T: Foo> Qux for Baz<T> {
fn print_bar() {
println!("bar: {}", T::BAR); // works
println!("{:?}", [T::BAR; 3]); // works
println!("{:?}", [1; FOOBAR]); // works
println!("{:?}", [1; T::BAR]); // this gives an error
}
}
fn main() {
Baz::<Fubar>::print_bar();
}
The compiler gives the following error:
error[E0599]: no associated item named `BAR` found for type `T` in the current scope
--> src/main.rs:24:30
|
24 | println!("{:?}", [1; T::BAR]); // this gives an error
| ^^^^^^ associated item not found in `T`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `BAR`, perhaps you need to implement it:
candidate #1: `Foo`
Whatever the answer to my question, this is not a particularly good error message because it suggests that T does implement Foo despite the latter being a trait bound. Only after burning a lot of time did it occur to me that in fact T::BAR is a perfectly valid expression in other contexts, just not as a length parameter to an array.
What are the rules that govern what kind of expressions can go there? Because arrays are Sized, I completely understand that the length are to be known at compile time. Coming from C++ myself, I would expect some restriction akin to constexpr but I have not come across that in the documentation where it just says
A fixed-size array, denoted [T; N], for the element type, T, and the non-negative compile-time constant size, N.
As of Rust 1.24.1, the array length basically needs to either be a numeric literal or a "regular" constant that is a usize. There's a small amount of constant evaluation that exists today, but it's more-or-less limited to basic math.
a perfectly valid expression in other contexts, just not as a length parameter to an array
Array lengths don't support generic parameters. (#43408)
this is not a particularly good error message
Error message should be improved for associated consts in array lengths (#44168)
I would expect some restriction akin to constexpr
This is essentially the restriction, the problem is that what is allowed to be used in a const is highly restricted at the moment. Notably, these aren't allowed:
functions (except to construct enums or structs)
loops
multiple statements / blocks
Work on good constant / compile-time evaluation is still ongoing. There are a large amount of RFCs, issues, and PRs improving this. A sample:
Const fn tracking issue (RFC 911)
Allow locals and destructuring in const fn (RFC 2341)
Allow if and match in constants (RFC 2342)

Typescript compiler won't allow methods on an union of two array types to be called

Please consider the following function:
function getArray(): string[] | number[] {
return [];
}
Long story short, when I do this:
getArray().forEach(item => console.log(item));
The compiler gives me this:
TS2349 Cannot invoke expression whose type lacks a call signature.
Verifiable example on TypeScript Playground
Why is this happening? They way I see it, forEach should be able to be called here without errors, as as far as the compiler is concerned, it is certain that an array will be returned. What is interesting is that IntelliSense will offer autocompletion for this method as usual, but after going with the autocomplete suggestion the error message will show in output.
Is this a bug, or am I missing something trivial here?
Edit: I could use various workarounds, like returning Array<string | number> instead, or using overloads, but I'm particularly interested in why returning an union of two array types disallow method invocation.
It is certain that the type is an array, with elements of type string | number.
In general, there's not a good "safe" way to process method calls of union types where the methods have different signatures (TypeScript will allow method calls when those methods have identical signatures) as if there were only one method.
Consider this example:
class ValueHaver<T> {
value: T;
update(x: () => T) { this.value = x(); }
}
let x: ValueHaver<number> | ValueHaver<string> = /*...*/;
x.update(() => 42);
This code is not correct. If x turned out to be ValueHaver<string>, then you'd have an number sitting where a string ought to be.
But this call is just as valid (presuming some hypothetical set of rules) as a forEach invocation of an Array<number> | Array<string>.
You might say "Well, you could try invoking the method X.y for each possible type of X in a union". In theory this could work, but in practice (and especially in TypeScript 2.0) you can easily get union types that have dozens of constituents. The only way to know that a specific type will work is to typecheck the entire call, and its arguments (because of contextual typing), all over again, which would be prohibitively expensive. And for technical reasons the TS compiler isn't generally capable of this -- once the type of a given expression is resolved, it's cached.
You cannot invoke forEach on the type string[] | number[].
What you probably want is: Array<string | number>:
function getArray(): Array<string | number> {
return [];
}
var test = getArray().forEach(item => { console.log(item) }); // no error
But if you prefer then you can cast to one of them:
(getArray() as string[]).forEach(item => { /* item is a string */ });
Or you can cast to any:
(getArray() as any[]).forEach(item => { /* item is any */ });

implicit conversion of an array of arrays

I have a case class called Point defined as follows:
case class Point(x: Double, y: Double)
and a functions that takes an array of points:
def f(coords: Array[Point]) ...
I want to be able to implictly pass an array of double arrays to my function. To achieve this I have defined the following two implicit functions:
implicit def arrayToPoint(a: Array[Double]) = new Point(a(0), a(1))
implicit def arraysToPoints(a: Array[Array[Double]]) = a map(p => Point(p(0), p(1)))
My questions is is there any way I can achieve this with just a single implict conversion function to simplify matters?
And as a related question what would be the best approach if I wanted to be able to pass an Array of Ints instead of Doubles?
Regards
Des
Your method arraysToPoints is superfluous. You could use a view bound on the array argument for your method f and add the conversion to the companion object of Point, like so:
object Point {
implicit def arrayToPoint[A](a: Array[A])(implicit view: A => Double): Point =
Point(a(0), a(1))
}
case class Point(x: Double, y: Double)
def f[P](coords: Array[P])(implicit view: P => Point): Unit = coords.foreach { p =>
println(p: Point)
}
f(Array(Point(1, 2), Point(2, 3)))
f(Array(Array(1.0, 2.0), Array(3.0, 4.0)))
f(Array(Array(1, 2), Array(3, 4)))
In order to allow both arrays of Int and Double to be covered, I have used a second view bound on the arrayToPoint method. Otherwise you would need two separate conversion methods for Array[Int] and Array[Double].
You can read this definition of f as, "take an array of elements of a type P which can be viewed as type Point". One spot where the compiler looks for such views is the companion object of the target type, thus object Point. This is a good place for implicit methods.
The second possibility would be to use the magnet pattern. Instead of converting point by point with a view in f, you would create a single wrapper object at once. This is a bit prettier and for large arrays minimises the penalty on direct Array[Double] arguments (because you instantiate the wrapper once, but then do not need to call the view function any more). arrayToPoint is used whenever the array element type A once again can be viewed as a Double. This is true for Double itself of course, but also for Int which can be seen as a Double though what Scala calls numeric widening (e.g., you can say val x: Double = 33 and the integer 33 is implicitly "widened" to a double).
object Points {
implicit def direct(a: Array[Point]): Points =
new Points {
val peer = a
}
implicit def indirect[A](a: Array[Array[A]])(implicit view: A => Double): Points =
new Points {
lazy val peer = a.map { c => Point(c(0), c(1)) }
}
}
trait Points {
def peer: Array[Point]
}
def f(coords: Points): Unit = coords.peer.foreach(println)
This looks in the companion object for an implicit method from the argument type to the special magnet type Points. I use lazy val for the non-direct arrays so that we might save the actual conversion action if the peer method is not called.

defining a new array of a specific type on F#

How can I specifically define a new array of a specific type T ?
Strangely, I couldn't find any helpful information about it..
I want to write something like this:
let arr = new Array()
only that the elements of arr must be of type T.
How can I do it on F# ?
If type of elements is unknown, you can use explicit type annotation:
let arrayOfTenZeroes = Array.zeroCreate<int> 10
let emptyIntArray: int array = [||]
When you use high-order functions from Array module, you don't have to do so since type of elements is automatically inferred by the type checker:
// arr is inferred as int array
let arr = Array.init 10 (fun i -> i+1)
Maybe the Array.init<'T> and Array.create<'T> functions are what you are looking for.
Also consider using a Sequence instead of an array.
A sequence is a logical series of elements all of one type. Sequences are particularly useful when you have a large, ordered collection of data but do not necessarily expect to use all the elements.
Perhaps you can try something like this.
let myStringArray : string array = Array.zeroCreate 10
let myIntArray : int array = Array.zeroCreate 10
let myCharArray : char array = Array.zeroCreate 10
It's described on msdn.
Automatic generalization and type inference are great features you should take advantage of.
When an array is created:
let arr = Array.zeroCreate 10
its type is generalized. In this case it's inferred to be 'T[] (as general as possible).
Once you do something like:
let x = arr.[0] + 1
or
printfn "%s" arr.[0]
it can infer the concrete type (int[] or string[], respectively).
The lack of explicit types makes your code much cleaner. Save type annotations for when they're truly needed.

Resources