Proposal to Add Implied Instances to the Language

I actually ended up just directly using context as a keyword in my proposal in the other thread. I also used derived for typeclasses, which is a keyword I’m less confident about, but overall just using method-style syntax on that side feels more natural.

I did a another trial run, this time with instance of instead of implied for. Here are the doc pages: instance definitions and the start page that links to all the other pages. instance of was originally proposed by @jdegoes, was implemented for a while, and then retired in favor of implied since we felt the name was too generic.

First reactions on the second try:

+ It reads well.

- instance is a very generic term, so I took pain to spell it out in the text everywhere as implicit instance. With a bit of discipline that should work. There’s precedence for that: Other definition keywords like def or type are also more generic than what they define precisely.

+ instance is immediately familiar for people coming from Haskell. It does not mean precisely the same thing in Scala (i.e. in Scala instance relates a type and a term, whereas in Haskell it relates a type class and a type), but that does not seem to be a problem.

- of probably has to be a soft keyword. That’s OK, even though it will mean we cannot define an instance with name of. That should be survivable, however.

Opinions?

1 Like

I still think that having both instance ... of and isInstanceOf in the language is too great a potential confusion since they are not actually directly related.

5 Likes

I think that the biggest advantage of instance of over implied for is that I can easily tell which is the new name and which is the typeclass. Also, implied just felt like a new keyword for the only purpose of not using implicit. It doesn’t really feel much more descriptive than instance to me.

Is there any use case for isInstanceOf which cannot be met by a type check in a pattern match?

If not, it might be a useful simplification to remove it from the language and migrate x.isInstanceOf[T] to:

x match {
   case _: T => true
   case _ => false
}

If there is a sort of a consensus that implied should be replaced with something better, maybe assumed is worth considering. Works with implied imports too, at least as well as current Dotty syntax.

assumed ListOrd[T] given (ord: Ord[T])

assumed IntOrd for Ord[Int]

assumed for Ord[Int] { ... }

Another one for the nouns that sound like legal terms, or Prolog

fact [A] given Show[A] of Show[List[A]] =
  _.map(_.show).mkString("[", ",", "]")

import fact A._
1 Like

There are a lot of positive aspects about choosing instance, in particular alignment with Haskell terminology. I just wonder whether the keywords instance and object are too much interchangeable semantically and thus confusing for newcomers?

Regarding the other choices I’d have a preference for the slightly more generic terms like implied, implicit or inferred over evidence or the original witness suggestion. While the explanation given for evidence makes absolute sense, I agree with some other posters that it is not necessarily intuitive without having those two paragraphs of explanation and I would suspect it might, like witness, bring a subtle vibe of obscurity for newcomers.

1 Like

Unless I’m mistaken, doesn’t Dotty still use the Scala 2 convention of declaration implicit vals, implicit val x = someValue? If so, I’m against using implicit to mimic val, and I also think using that word in implicit x for Foo[Int]is less ideal than using implied or some unique keyword. The less ambiguity introduced the better in my opinion.

1 Like

This. Instance sounds good in isolation, but instance pretty universally means the same as object in large swaths of the JVM ecosystem (e. g. Java), so it might be quite confusing for people.

Also, it would be nice if the syntax would tell more about this construct than just that it is an instance, and most other proposals already accomplish this.

I also agree on this, while the explanation for evidence makes sense, it would feel alien (alienating?) to a lot of people, again the average-ish workplaces I referenced elsewhere.

In Java world object means generally the same as instance of a class. Scala’s object is a singleton (i.e. sole instance) of an anonymous class. Therefore there’s already a confusion. instance keyword would add another ambiguity when confronted with standard OOP terminology. Rust uses impl which doesn’t directly clash with anything Java or Scala related, so it would be a good candidate (unless you’re using a weird and useless convention like class RedundantInterfaceImpl extends RedundantInterface instead of just class NoArtificialHierarchy).

1 Like

The arguments that instance and object are interchangeable sunk the proposal the last time. But I am no longer convinced that they are incontrovertible. When I write

instance IntOrd of Ord[Int]

I do define an instance (in the sense of object) of type Ord[Int]. So the wording is not wrong, it’s just too generic. The wording does not imply that the instance I define is the canoncial, or implicit one. That can be mitigated by writing systematically implicit instance in all accompanying text, so that the two words get associated more closely.

There’s precedent to have generic words mean something more specialized. For instance, object in Scala. It means not an object in general, but a lazily instantiated singleton value of an anonymous class. Should we have used singleton instead? Maybe. But somehow, object worked well and is easier to remember. Or, take impl in Rust. It has a role very much like instance, but the word really means an arbitrary implementation.

I agree that the overlap with isInstanceOf/asInstanceOf is unfortunate. But we could also investigate whether we should come up with alternatives for isInstanceOf/asInstanceOf.

2 Likes

Perhaps…
a.asInstanceOf[B] to (a :! B) as unique syntax or a:![B] as a def
a.isInstanceOf[B] to (a :? B) as unique syntax or a:?[B] as a def

One thing that I would like to correct is remove the symmetry between isInstanceOf and asInstanceOf. The two behave not the same, in the sense that there are situations where
e.isInstanceOf[T] gives false but e.asInstanceOf[T] succeeds. This could happen in a number of situations: if e is null, or if T is a union or intersection type, or is prefixed such as in T = p.C.

x.isInstanceOf[T] means essentially: Apply the same algorithm as in pattern matching to determine whether x is a T. It’s not perfect because of erasure. But what can be reasonably checked will be.

x.asInstanceOf[T] means essentially: Trust me, I know what I am doing. As long as the JVM allows to cast x to the erasure of type T the operation will succeed.

So the two operations are quite different, and intentionally so. Which means that the symmetry between their names raises false expectations. In my experience, many people are confused by this.

3 Likes

IIRC long names like isInstanceOf and asInstanceOf were chosen to discourage using them. Changing them to short operators will increase their popularity which probably is not what we want.

isInstanceOf and asInstanceOf are as low level as they can be, so they are used for performance purposes. Pattern matching sometimes adds some overhead, but in typical case it should not be a concern of a programmer.

Maybe put those operations behind a language flag? I think they are used very rarely in business logic. Libraries tend to have some isInstanceOf and asInstanceOf, but they also tend to have uncheckedVariance and other hacks.

I have come around on given going last in instance definitions. I believe my German language background made me tolerant of subsidiary causes like given in the middle of a sentence (germans love stack machines!) but that’s not shared by everyone. So in the new pages it’s instance ... of ... given. It does read better.

There’s some potential ambiguity with a given clause on an implemented constructor, which has to be resolved with parentheses. I.e.

instance a of B given C

means a is an implicit instance of B provided that C holds, whereas

instance a of (B given c)

means that a is an implicit instance of B, to which it passes c as implicit evidence. The second case should be very rare, as usually any evidence passed to the superclass is already evidence in the class itself, so the argument would be left implicit.

3 Likes

Would it not be correct behaviour for e.isInstanceOf[T] to give true if e is null and T <: AnyRef?

That’s a long discussion which goes back to Java. I don’t think we want to revive it here.

As a native American English speaker that updated ordering does read much better to me but… it’s always fascinating to realize how drastically one’s background can affect these things

1 Like

I’m late to this discussion, but I wanted to add another voice on the matter: I’m so glad you said this, @lihaoyi. I find that reusing for as a keyword in this sense can only lead to potential confusion; confusion that’s easily resolved, probably once for every beginner, but that’s still an O(n) problem rather than an O(1) problem if we chose a different keyword now.

In addition to @lihaoyi’s comments about users’ understanding of the keyword for, I think that it would be a bigger problem that it’s being used for two completely different purposes in the same language. There’s an obvious question: what is the relationship between for-comprehensions and this new for? One criticism made of the keyword implicit is that the same keyword is used in different contexts (parameters and definitions). It would be ironic to introduce a new ambiguity in the solution.

In my opinion, instance of would be fine, as would a couple of the other choices. None of them bring me out in a “Wadler’s Law” rash like for does!

5 Likes