Scala 3 multiversal equality of type parameters - scala

In Scala 3, with -language:strictEquality, this code:
trait X[A]:
def val1: A
def val2: A
def f() =
if val1 == val2 then
println("Same")
produces this error:
Values of types A and A cannot be compared with == or !=
I looked for some trait that would tell the compiler that A can be compared to itself, but couldn't find one. The solution I found was to add this line:
given CanEqual[A, A] = CanEqual.derived
However, I still have several questions:
How come you can't compare two instances of the same type? What are some instances of A where val1 == val2 would not be legal?
Is there a trait that would add ==, != to a type?
If the compiler can't figure out that you can compare A to A when A is a type parameter, then how come given CanEqual[A, A] = CanEqual.derived keeps it happy? What is the magic in CanEqual.derived and why can't it be applied by default when using ==?

This comparison is not really illegal, it is just undefined.
The given solution is more flexible than a trait, but you can just define == on your type if you prefer.
You have explicitly told the compiler that you don't want it to generate a default == operator. That's what strictEquality means.
The logic behind strictEquality is that there are different kinds of equality and the compiler shouldn't arbitrarily chose one kind. Objects can be compared by reference or by contents, and it neither is "correct" in every case.
Without strictEquality the compiler implements referential equality by default, which means two values are the same only if they refer to the same object. case class implements structural equality by checking each field for equality, so two different instances are "equal" if all the values of all the fields are the same. Other classes can implement equality by comparing only a relevant subset of field, or by other criteria.
According to the documentation strict equality is opt-in for for compatibility and migration reasons.

Without -language:strictEquality, these == calls are actually handled by equals(Object other) method defined on Java Object class.
So, it used either the overridden equals method for your class or the defualt equals from Object class. This often results in unexpected behaviours.
Adding -language:strictEquality changes this behaviour, and == are handled using the typeclass CanEqual[A, A].
To ensure that you are using correct equality, the design decision was to force you to explicitly provide that CanEqual[A, A].
Now, there is no default catch all way to compare two values. If you want anything to be compared, be ready to provide how to comapre as well.
The comparision is not illegal in any way, but the language just makes no assumptions on how two values should be compared.

Related

Disfunctionality of type parameter

I’m new to using Scala and am trying to see if a list contains any objects of a certain type.
When I make a method to do this, I get the following results:
var l = List("Some string", 3)
def containsType[T] = l.exists(_.isInstanceOf[T])
containsType[Boolean] // val res0: Boolean = true
l.exists(_.isInstanceOf[Boolean]) // val res1: Boolean = false
Could someone please help me understand why my method doesn’t return the same results as the expression on the last line?
Thank you,
Johan
Alin's answer details perfectly why the generic isn't available at runtime. You can get a bit closer to what you want with the magic of ClassTag, but you still have to be conscious of some issues with Java generics.
import scala.reflect.ClassTag
var l = List("Some string", 3)
def containsType[T](implicit cls: ClassTag[T]): Boolean = {
l.exists(cls.runtimeClass.isInstance(_))
}
Now, whenever you call containsType, a hidden extra argument of type ClassTag[T] gets passed it. So when you write, for instance, println(containsType[String]), then this gets compiled to
scala.this.Predef.println($anon.this.containsType[String](ClassTag.apply[String](classOf[java.lang.String])))
An extra argument gets passed to containsType, namely ClassTag.apply[String](classOf[java.lang.String]). That's a really long winded way of explicitly passing a Class<String>, which is what you'd have to do in Java manually. And java.lang.Class has an isInstance function.
Now, this will mostly work, but there are still major caveats. Generics arguments are completely erased at runtime, so this won't help you distinguish between an Option[Int] and an Option[String] in your list, for instance. As far as the JVM is concerned, they're both Option.
Second, Java has an unfortunate history with primitive types, so containsType[Int] will actually be false in your case, despite the fact that the 3 in your list is actually an Int. This is because, in Java, generics can only be class types, not primitives, so a generic List can never contain int (note the lowercase 'i', this is considered a fundamentally different thing in Java than a class).
Scala paints over a lot of these low-level details, but the cracks show through in situations like this. Scala sees that you're constructing a list of Strings and Ints, so it wants to construct a list of the common supertype of the two, which is Any (strings and ints have no common supertype more specific than Any). At runtime, Scala Int can translate to either int (the primitive) or Integer (the object). Scala will favor the former for efficiency, but when storing in generic containers, it can't use a primitive type. So while Scala thinks that your list l contains a String and an Int, Java thinks that it contains a String and a java.lang.Integer. And to make things even crazier, both int and java.lang.Integer have distinct Class instances.
So summon[ClassTag[Int]] in Scala is java.lang.Integer.TYPE, which is a Class<Integer> instance representing the primitive type int (yes, the non-class type int has a Class instance representing it). While summon[ClassTag[java.lang.Integer]] is java.lang.Integer::class, a distinct Class<Integer> representing the non-primitive type Integer. And at runtime, your list contains the latter.
In summary, generics in Java are a hot mess. Scala does its best to work with what it has, but when you start playing with reflection (which ClassTag does), you have to start thinking about these problems.
println(containsType[Boolean]) // false
println(containsType[Double]) // false
println(containsType[Int]) // false (list can't contain primitive type)
println(containsType[Integer]) // true (3 is converted to an Integer)
println(containsType[String]) // true (class type so it works the way you expect)
println(containsType[Unit]) // false
println(containsType[Long]) // false
Scala uses the type erasure model of generics. This means that no
information about type arguments is kept at runtime, so there's no way
to determine at runtime the specific type arguments of the given
List object. All the system can do is determine that a value is a
List of some arbitrary type parameters.
You can verify this behavior by trying any List concrete type:
val l = List("Some string", 3)
println(l.isInstanceOf[List[Int]]) // true
println(l.isInstanceOf[List[String]]) // true
println(l.isInstanceOf[List[Boolean]]) // also true
println(l.isInstanceOf[List[Unit]]) // also true
Now regarding your example:
def containsType[T] = l.exists(_.isInstanceOf[T])
println(containsType[Int]) // true
println(containsType[Boolean]) // also true
println(containsType[Unit]) // also true
println(containsType[Double]) // also true
isInstanceOf is a synthetic function (a function generated by the Scala compiler at compile-time, usually to work around the underlying JVM limitations) and does not work the way you would expect with generic type arguments like T, because after compilation, this would normally be equivalent in Java to instanceof T which, by the way - is illegal in Java.
Why is illegal? Because of type erasure. Type erasure means all your generic code (generic classes, generic methods, etc.) is converted to non-generic code. This usually means 3 things:
all type parameters in generic types are replaced with their bounds or Object if they are unbounded;
wherever necessary the compiler inserts type casts to preserve type-safety;
bridge methods are generated if needed to preserve polymorphism of all generic methods.
However, in the case of instanceof T, the JVM cannot differentiate between types of T at execution time, so this makes no sense. The type used with instanceof has to be reifiable, meaning that all information about the type needs to be available at runtime. This property does not apply to generic types.
So if Java forbids this because it can't work, why does Scala even allows it? The Scala compiler is indeed more permissive here, but for one good reason; because it treats it differently. Like the Java compiler, the Scala compiler also erases all generic code at compile-time, but since isInstanceOf is a synthetic function in Scala, calls to it using generic type arguments such as isInstanceOf[T] are replaced during compilation with instanceof Object.
Here's a sample of your code decompiled:
public <T> boolean containsType() {
return this.l().exists(x$1 -> BoxesRunTime.boxToBoolean(x$1 instanceof Object));
}
Main$.l = (List<Object>)package$.MODULE$.List().apply((Seq)ScalaRunTime$.MODULE$.wrapIntArray(new int[] { 1, 2, 3 }));
Predef$.MODULE$.println((Object)BoxesRunTime.boxToBoolean(this.containsType()));
Predef$.MODULE$.println((Object)BoxesRunTime.boxToBoolean(this.containsType()));
This is why no matter what type you give to the polymorphic function containsType, it will always result in true. Basically, containsType[T] is equivalent to containsType[_] from Scala's perspective - which actually makes sense because a generic type T, without any upper bounds, is just a placeholder for type Any in Scala. Because Scala cannot have raw types, you cannot for example, create a List without providing a type parameter, so every List must be a List of "something", and that "something" is at least an Any, if not given a more specific type.
Therefore, isInstanceOf can only be called with specific (concrete) type arguments like Boolean, Double, String, etc. That is why, this works as expected:
println(l.exists(_.isInstanceOf[Boolean])) // false
We said that Scala is more permissive, but that does not mean you get away without a warning.
To alert you of the possibly non-intuitive runtime behavior, the Scala compiler does usually emit unchecked warnings. For example, if you had run your code in the Scala interpreter (or compile it using scalac), you would have received this:

The purpose of type classes in Haskell vs the purpose of traits in Scala

I am trying to understand how to think about type classes in Haskell versus traits in Scala.
My understanding is that type classes are primarily important at compile time in Haskell and not at runtime anymore, on the other hand traits in Scala are important both at compile time and run time. I want to illustrate this idea with a simple example, and I want to know if this viewpoint of mine is correct or not.
First, let us consider type classes in Haskell:
Let's take a simple example. The type class Eq.
For example, Int and Char are both instances of Eq. So it is possible to create a polymorphic List that is also an instance of Eq and can either contain Ints or Chars but not both in the same List.
My question is : is this the only reason why type classes exist in Haskell?
The same question in other words:
Type classes enable to create polymorphic types ( in this example a polymorphic List) that support operations that are defined in a given type class ( in this example the operation == defined in the type class Eq) but that is their only reason for existence, according to my understanding. Is this understanding of mine correct?
Is there any other reason why type classes exist in ( standard ) Haskell?
Is there any other use case in which type classes are useful in standard Haskell ? I cannot seem to find any.
Since Haskell's Lists are homogeneous, it is not possible to put Char and Int into the same list. So the usefulness of type classes, according to my understanding, is exhausted at compile time. Is this understanding of mine correct?
Now, let's consider the analogous List example in Scala:
Lets define a trait Eq with an equals method on it.
Now let's make Char and Int implement the trait Eq.
Now it is possible to create a List[Eq] in Scala that accepts both Chars and Ints into the same List ( Note that this - putting different type of elements into the same List - is not possible Haskell, at least not in standard Haskell 98 without extensions)!
In the case of the Haskell's List, the existence of type classes is important/useful only for type checking at compile time, according to my understanding.
In contrast, the existence of traits in Scala is important both at compile time for type checking and at run type for polymorphic dispatch on the actual runtime type of the object in the List when comparing two Lists for equality.
So, based on this simple example, I came to the conclusion that in Haskell type classes are primarily important/used at compilation time, in contrast, Scala's traits are important/used both at compile time and run time.
Is this conclusion of mine correct?
If not, why not ?
EDIT:
Scala code in response to n.m.'s comments:
case class MyInt(i:Int) {
override def equals(b:Any)= i == b.asInstanceOf[MyInt].i
}
case class MyChar(c:Char) {
override def equals(a:Any)= c==a.asInstanceOf[MyChar].c
}
object Test {
def main(args: Array[String]) {
val l1 = List(MyInt(1), MyInt(2), MyChar('a'), MyChar('b'))
val l2 = List(MyInt(1), MyInt(2), MyChar('a'), MyChar('b'))
val l3 = List(MyInt(1), MyInt(2), MyChar('a'), MyChar('c'))
println(l1==l1)
println(l1==l3)
}
}
This prints:
true
false
I will comment on the Haskell side.
Type classes bring restricted polymorphism in Haskell, wherein a type variable a can still be quantified universally, but ranges over only a subset of all the types -- namely, the types for which an instance of the type class is available.
Why restricted polymorphism is useful? A nice example would be the equality operator
(==) :: ?????
What its type should be? Intuitively, it takes two values of the same type and returns a boolean, so:
(==) :: a -> a -> Bool -- (1)
But the typing above is not entirely honest, since it allows one to apply == to any type a, including function types!
(\x :: Integer -> x + x) == (\x :: Integer -> 2*x)
The above would pass type checking if (1) were the typing for (==), since both arguments are of the same type a = (Integer -> Integer). However, we can not effectively compare two functions: well-known Computability results tell us that there is no algorithm to do that in general.
So, what we could do to implement (==)?
Option 1: at run time, if a function (or any other value involving functions -- such as a list of functions) is found to be passed to (==), raise an exception. This is what e.g. ML does. Typed programs can now "go wrong", despite checking types at compile time.
Option 2: introduce a new kind of polymorphism, restricting a to the function-free types. For instance, ww could have (==) :: forall-non-fun a. a -> a -> Bool so that comparing functions yields to a type error. Haskell exploits type classes to obtain exactly that.
So, Haskell type classes allow one to type (==) "honestly", ensuring no error at run time, and without being overly restrictive. Of course, the power of type classes goes far beyond of that but, at least in my own view, they primary purpose is to allow restricted polymorphism, in a very general and flexible way. Indeed, with type classes the programmer can define their own restrictions on the universal type quantifications.

What are the reasons and/or benefits Scala can compare incompatible objects?

This puzzles me -- I have read the reasons Scala exists at all, and the common-sense presented appeals to me, for example choosing static typing (because of less errors). Yet, you can compare (out of the box, by default) completely different, irrelevant objects and it compiles and runs fine. For me it just begs for more errors in code.
Could someone please explain what are the reasons for such feature? Or benefits?
I know how Scala works in the matter of comparison. I am asking WHY it works that way.
I would expect, that if I want to do this, I would write implicit conversion or explicit comparison. That approach makes perfect sense for me, current Scala way -- no, and thus my question.
And one more thing -- I am not looking how to use Scala-way comparison for some fancy effects, I am looking for more strict error checking. IOW: I don't want to compare Orange and Apple by color, I want to forbid such comparison by default unless user explicitly say it is OK to compare such types.
Example
class Test
{
val s : String = "ala"
}
class Foo
{
val x : Int = 5
}
object Testbed
{
def main(args : Array[String])
{
val t = new Test
val f = new Foo
if (t==f)
println("match")
else
println("no")
}
}
Well, the simple answer is that == is designed to be compatible with java.lang.Object.equals(). Since any class can override equals() it's impossible for the Scala compiler to determine the result of an equality check between two objects unless it knows the runtime class of the object at compile time (i.e. the static type must be a final class or the new invocation must be visible to the compiler when compiling the equality check) AND the equals() method is not overridden in this class or any super class.
So, in your example the compiler can indeed infer the runtime class of both t and f and issue a warning (not an error though) for the equality check, but in practice the cases where the runtime class can be inferred are quite rare. Note that scalac already issues warnings for some equality comparisons between primitive types and object types.
P.S. If you want a safer equality check use the Equal trait in Scalaz.
Equality in scala is value equality (not reference) and what is equal to what can be defined by you by overriding equals. Basically == is not an operator in scala, but acts like (and in fact IS) a method on the object that you want to test for equality.
So t == f is actually t.==(f) where == is defined on Any, so you get it on every class in scala.
For instance (in your example) you can make your Test == to a Foo, like this:
class Test {
val s : String = "ala"
}
class Foo {
val x : Int = 5
override def equals(that: Any) : Boolean = {
that.isInstanceOf[Test] && this.x == 5 && that.asInstanceOf[Test].s=="ala";
}
}
and now you get:
scala> val t = new Test
t: Test = Test#86a58a
scala> val f = new Foo
f: Foo = Foo#104f889
scala> f==t
res3: Boolean = true
but (since we have NOT overridden equals on Test)
scala> t==f
res4: Boolean = false
While in this specific case this does not make a lot of sense, the point is that scala lets you decide what makes a Test equal to a Foo. Do you want a Person to be == to another Person (or to an Employee) if they have the same social security number? You can implement that logic.
However, with great power comes great responsibility and in fact the concept of equality is surprisingly tricky.
In recent trunk builds your example warns. I think I found a good balance between likelihood of mis-warning and catching unintended comparisons. It's harder than it looks in the presence of universal equality and subtyping.
% scalac3 ./a.scala
./a.scala:20: warning: Test and Foo are unrelated: they will most likely never compare equal
if (t==f)
^
one warning found
I am looking for more strict error checking.
The recent (May 2016) post "Multiversal Equality for Scala", written by the creator of Scala himself Martin Odersky, is trying to address that.
The current status in Scala is that the compiler will give warnings for some comparisons that are always false. But the coverage is weak. For instance this will give a warning:
scala> 1 == "abc"
<console>:12: warning: comparing values of types Int and String using `==' will always yield false
But this will not:
scala> "abc" == 1
res2: Boolean = false
I believe to do better, we need to enlist the cooperation of developers.
Ultimately it’s the developer who provides implementations of equality methods and who is therefore best placed to characterize which equalities make sense.
The best known way to characterize such relationships is with type classes.
Implicit values of a trait Eq[T, U] can capture the property that values of type T can be compared to values of type U.
Here’s the definition of Eq:
package scala
trait Eq[-T, -U]
Given a set of Eq instances, the idea is that the Scala compiler will check every time it encounters a potentially problematic comparison between values of types T and U that there is an implicit instance of Eq[T, U].
A comparison is potentially problematic if it is between incompatible types.
As long as T <: U or U <: T the equality could make sense because both sides can potentially be the same value.

How to constrain a type to a subset of another?

I have that this is a dumb question feeling, but here goes ... Can I define a type that is a subset of the elements of a another type? Here's a simplified example.
scala> class Even(i: Int) {
| assert(i % 2 == 0)
| }
defined class Even
scala> new Even(3)
java.lang.AssertionError: assertion failed
This is a runtime check. Can I define a type such that this is checked at compilation? IE, that the input parameter i is provably always even?
Value-dependent typing in languages such as Coq and Agda can do this, though not Scala.
Depending on the exact use-case, there are ways of encoding peano numbers in the type system that may, however, help you.
You might also want to try defining both Even and Odd along with some sealed abstract supertype (OddOrEven perhaps) and a factory method that returns the correct instance from any given Integer.
Another possibility is to define Even as an extractor.

Scala: Why does Seq.contains take an Any argument, instead of an argument of the sequence type?

So for example why does List(1,2,3,4).contains("wtf") even compile? Wouldn't it be nice if the compiler rejected this?
Lots of interesting answers, but here's my own theory: if contains did not receive an Any, then Seq could not be co-variant.
See, for instance, Set, which is not co-variant and whose contains take an A instead of an Any.
The reasons for that is left as an exercise to the reader. ;-) But here is a hint:
scala> class Container[+A](elements: A*) {
| def contains(what: A): Boolean = elements exists (what ==)
| }
<console>:7: error: covariant type A occurs in contravariant position in type A of value what
def contains(what: A): Boolean = elements exists (what ==)
^
"contains" is fundamentally about equality testing, and equality in Scala (as in Java before it) is untyped. The practical value of having untyped-equality is small, but not zero. There are, for instance, a few occasions where it makes sense for two objects of different classes to be equal to one another. For instance, you might wish an object of type RGBColor to be equal to a PantoneColor if they define the same hue, or an immutable HashSet and an immutable TreeSet to be equal if they contain the same elements. That said, untyped-equality also causes a bunch of headaches, and the fact that the compiler could easily catch that List(1,2,3,4).contains("wtf") is nonsensical but won't is one of them.
Most Java bug-finding tools include tests to detect the presence of improbable untyped-equality uses. (I wrote the inspections to do this in IntelliJ IDEA.) I have no doubt that when Scala bug-finding tools come online, these will be among the first bugs detected.
SeqLike.contains checks whether a value is present by checking for an element in the sequence that is equal to the value (using ==). == takes an Any so I suspect that this is the reason.