Skip to content

Commit 2262157

Browse files
committed
Merge branch 'master' of https://github.com/scala/scala-lang into announcing-moocs
2 parents ffca216 + dedab53 commit 2262157

File tree

2 files changed

+165
-18
lines changed

2 files changed

+165
-18
lines changed

README.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,26 @@ This site uses a Jekyll, a Ruby framework. The required Jekyll version is 1.5.1.
1212

1313
There are two ways to run Jekyll to build the site:
1414

15-
* using globally installed Jekyll and accompanying gems
16-
* using Bundler, so Jekyll and accompanying gems are installed only inside this directory
15+
1. using Bundler, so Jekyll and accompanying gems are installed only inside this directory
16+
2. using globally installed Jekyll and accompanying gems
1717

18-
The former method is the one currently actually used on
19-
scala-lang.org. The latter method may be more convenient for users who
20-
are comfortable using Bundler and who don't want anything else
21-
installed system-wide.
18+
The latter method is the one currently actually used on scala-lang.org. The
19+
former method is likely most convenient for users who already have a different
20+
version of Jekyll installed, or who are comfortable using Bundler and who don't
21+
want anything else installed system-wide.
2222

23-
### Building with global Jekyll
23+
### Option 1) Building with Bundler
24+
25+
`cd` into the directory where you cloned this repository, then install the required gems with `bundle install`. This will automatically put the gems into `./bundle-vendor/bundle`.
26+
27+
Start the server in the context of the bundle:
28+
29+
bundle exec jekyll serve
30+
31+
from this point, everything else should be the same, regardless of which method
32+
you used to run Jekyll.
33+
34+
### Option 2) Building with global Jekyll
2435

2536
Install Jekyll 1.5.1 on your system using RubyGems:
2637

@@ -39,17 +50,6 @@ and watch the output. You should see something like:
3950
Generating... done.
4051
Auto-regeneration: enabled for '/Users/ben/src/scala-lang'
4152

42-
### Building with Bundler
43-
44-
`cd` into the directory where you cloned this repository, then install the required gems with `bundle install`. This will automatically put the gems into `./bundle-vendor/bundle`.
45-
46-
Start the server in the context of the bundle:
47-
48-
bundle exec jekyll serve
49-
50-
from this point, everything else should be the same, regardless of which method
51-
you used to run Jekyll.
52-
5353
### Windows and UTF-8
5454

5555
If you get `incompatible encoding` errors when generating the site under Windows, then ensure that the
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
---
2+
layout: blog
3+
post-type: blog
4+
by: Martin Odersky
5+
title: Multiversal Equality for Scala
6+
disqus: true
7+
---
8+
9+
I have been working recently on making equality tests using `==` and
10+
`!=` safer in Scala. This has led to a [Language Enhancement
11+
Proposal](https://github.com/lampepfl/dotty/issues/1247) which I summarize in this blog.
12+
13+
## Why Change Equality?
14+
15+
Scala prides itself of its strong static type system. Its type discipline is particularly useful when it comes to refactoring. Indeed, it's possible to write programs in such a way that refactoring problems show up with very high probability as type errors. This is essential for being able to refactor with the confidence that nothing will break. And the ability to do such refactorings is in turn very important for keeping code bases from rotting.
16+
17+
Of course, getting such a robust code base requires the cooperation of the developers. They should avoid type `Any`, casts, [stringly typed](http://c2.com/cgi/wiki?StringlyTyped) logic, and more generally any operation over loose types that do not capture the important properties of a value. Unfortunately, there is one area in Scala where such loose types are very hard to avoid: That's equality. Comparisons with `==` and `!=` are _universal_. They compare any two values, no matter what their types are. This causes real problems for writing code and more problems for refactoring it.
18+
19+
For instance, one might want to introduce a proxy for some data structure so that instead of accessing the data structure directly one goes through the proxy. The proxy and the underlying data would have different types. Normally this should be an easy refactoring. If one passes by accident a proxy for the underlying type or _vice versa_ the type checker will flag the error. However, if one accidentally compares a proxy with the underlying type using `==` or a pattern match, the program is still valid, but will just always say `false`. This is a real worry in practice. I recently abandoned a desirable extensive refactoring because I feared that it would be too hard to track down such errors.
20+
21+
## Where Are We Today?
22+
23+
The problems of universal equality in Scala are of course well
24+
known. Some libraries have tried to fix it by adding another equality
25+
operator with more restricted typing. Most often this safer equality
26+
is written `===`. While `===` is certainly useful, I am not a fan of
27+
adding another equality operator to the language and core
28+
libraries. It would be much better if we could fix `==` instead. This
29+
would be both simpler and would catch all potential equality problems
30+
including those related to pattern matching.
31+
32+
How can `==` be fixed? It looks much harder to do this than adding an
33+
alternate equality operator. First, we have to keep backwards
34+
compatibility. The ability to compare everything to everything is by
35+
now baked into lots of code and libraries.
36+
Second, with just one equality operator
37+
we need to make this operator work in all cases where it makes
38+
sense. An alternative `===` operator can choose to refuse some
39+
comparisons that should be valid because there's always `==`
40+
to fall back to. With a unique `==` operator we do not have this
41+
luxury.
42+
43+
The current status in Scala is that the compiler will give warnings
44+
for _some_ comparisons that are always `false`. But the coverage is
45+
weak. For instance this will give a warning:
46+
47+
scala> 1 == "abc"
48+
<console>:12: warning: comparing values of types Int and String using `==' will always yield false
49+
50+
But this will not:
51+
52+
scala> "abc" == 1
53+
res2: Boolean = false
54+
55+
There are also cases where a warning is given for a valid equality
56+
test that actually makes sense because the result could be `true`. In
57+
summary, the current checking catches some obvious bugs, which is
58+
nice. But it is far too weak and fickle to be an effective refactoring
59+
aid.
60+
61+
62+
## What's Proposed?
63+
64+
I believe to do better, we need to enlist the cooperation of
65+
developers. Ultimately it's the developer who provides implementations
66+
of equality methods and who is therefore best placed to characterize
67+
which equalities make sense. Sometimes this characterization can be
68+
involved. For instance, an `Int` can be compared to other primitive
69+
numeric values or to instances of type `java.lang.Number` but any other
70+
comparison will always yield `false`. Or, it makes sense to compare
71+
two `Option` values if and only if it makes sense to compare the optional
72+
element values.
73+
74+
The best known way to characterize such relationships is with type
75+
classes. Implicit values of a trait `Eq[T, U]` can capture the
76+
property that values of type `T` can be compared to values of type
77+
`U`. Here's the definition of `Eq`
78+
79+
package scala
80+
81+
trait Eq[-T, -U]
82+
83+
That is, `Eq` is a pure marker trait with two type parameters and without
84+
any members. Developers can define equality classes by giving
85+
implicit `Eq` instances. Here is a simple one:
86+
87+
implicit def eqString: Eq[String, String] = Eq
88+
89+
This states that strings can be only compared to strings, not to values of other types.
90+
Here's a more complicated `Eq` instance:
91+
92+
implicit def eqOption[T, U](implicit _eq: Eq[T, U]): Eq[Option[T], Option[U]] = Eq
93+
94+
This states that `Option` values can be compared if their elements can be compared.
95+
96+
It's foreseen that such `Eq` instances can be generated automatically. If we add
97+
an annotation `@equalityClass` to `Option` like this
98+
99+
@equalityClass class Option[+T] { ... }
100+
101+
then the `eqOption` definition above would be generated automatically in `Option`'s companion object.
102+
103+
Given a set of `Eq` instances, the idea is that the Scala
104+
compiler will check every time it encounters a _potentially
105+
problematic_ comparison between values of types `T` and `U` that there
106+
is an implicit instance of `Eq[T, U]`. A comparison is _potentially
107+
problematic_ if it is between incompatible types. As long as `T <: U`
108+
or `U <: T` the equality could make sense because both sides can
109+
potentially be the same value.
110+
111+
So this means we still keep universal equality as it is in Scala now
112+
- we don't have a choice here anyway, because of backwards
113+
compatibility. But we render it safe by checking that for each
114+
comparison the corresponding `Eq` instance exists.
115+
116+
What about types for which no `Eq` instance exists? To maintain
117+
backwards compatibility, we allow comparisons of such types as well,
118+
by means of a fall-back `eqAny` instance. But we do not allow comparisons
119+
between types that have an `Eq` instance and types that have none.
120+
Details are explained in the
121+
[proposal](https://github.com/lampepfl/dotty/issues/1247).
122+
123+
## Properties
124+
125+
Here are some nice properties of the proposal
126+
127+
1. It is _opt-in_. To get safe checking, developers have to annotate with `@equalityClass` classes that should
128+
allow comparisons only between their instances, or they have to define implicit
129+
`Eq` instances by hand.
130+
2. It is backwards compatible. Without developer-provided `Eq` instances, equality works as before.
131+
3. It carries no run-time cost compared to universal equality. Indeed the run-time behavior of
132+
equality is not affected at all.
133+
4. It has no problems with parametricity, variance, or bottom types.
134+
5. Depending on the actual `Eq` instances given, it can be very precise. That is,
135+
no comparisons that might yield `true` need to be rejected, and most comparisons that
136+
will always yield `false` are in fact rejected.
137+
138+
The scheme effectively leads to a partition of the former universe of
139+
types into sets of types. Values with types in the same partition can
140+
be compared among themselves but values with types in different
141+
partitions cannot.
142+
An `@equalityClass` annotation on a type creates a new partition. All
143+
types that do not have any `Eq` instances (except `eqAny`, that is)
144+
form together another partition.
145+
So instead of a single _universe_ of values that can be compared to
146+
each other we get a _multiverse_ of partitions. Hence the name of the
147+
proposal: **Multiversal Equality**.

0 commit comments

Comments
 (0)