Proposal to Add Implied Instances to the Language

#21

I have prepared two alternative sets of doc pages, one replacing implied with evidence and the other replacing it with implicit. The start pages for these are here:

In both cases, skip ahead to “The New Design” to get to the links to the individual pages in the group.

Here are direct links to the pages that would replace the implied instances page proposed at the start of this thread: evidence definitions, implicit instances.

My observations so far:

  • both designs look OK, overall.
  • implicit looks more familiar (no surprise here!)
  • On the other hand, it is considerably harder to talk about new style vs old style implicits if the keyword is the same. For instance, this makes it awkward to formulate rules for import implicit.
  • It’s also harder to distinguish old-style and new-style implicits in code, which could become confusing.
  • evidence works better as a noun, but implicit is also OK, because of widespread existing usage as a noun.
  • evidence works better in conjunction with other nouns. I.e. evidence import, evidence definition, evidence alias is clearer than implicit import, implicit definition, implicit alias.
  • In the text, we sometimes have to accommodate for the fact that it is awkward to express plurality of evidences. One can say “the implicits in scope”, but “the evidences in scope” feels off to me, it has to be either “the evidence in scope” in singular or “the evidence values in scope”.

My current feeling is that I prefer either variant slightly over implied: implicit because it is more familiar and evidence because it is a bit more precise, and also because it makes it easier to talk about the differences. The deciding question is ultimately wether similarity with existing implicits is an advantage or a handicap.

2 Likes
#22

One more thing I’d like to chip is that I am viscerally opposed to the use of the for keyword.

It doesn’t matter what for means in english, whether the english meaning fits or not is irrelevant. for in Scala, and for in every other programming language, means something else entirely: looping or traversal over a collection (generalized to monad/functor in Scala).

extends is the perfect keyword to use here, since we are literally defining an instance of a type, and object Foo extends Bar is how you define an instance of a type. implicit Foo extends Bar{ ... } with implicit Foo extends Bar = ... or implicit Foo: Bar = ... all flow very naturally for how someone would expect Scala to look like.

But even if you don’t want to use extends, please do not use for. Come up with a new keyword, come up with some operator, just don’t overload the for keyword that every programming in the last 50 years knows to mean something, and make it mean something entirely different!

6 Likes
#23

I tend to agree with this. What about @, which means “at condition”?

implicit x @ Foo[Int] { ... }
#24

I really like the evidence one, but indeed implicit is more familiar and in a good way. For me, either variation is better than implied.

1 Like
#25

I also do not think evidence is the right way to go. evidence sounds good for people with a type-theory, formal-logic or theorem-proving background. It sounds completely meaningless to people with a “normal programming” background, coming from Java, Javascript, Python, C, C++, C#, or any other language in the RedMonk top 20.

If we are hoping to attract more formal-logic or theorem-proving people to Scala, evidence would be a great fit. If we want to attract more "normal programmer"s to Scala, it is a pretty awful spelling for a relatively common idea (“implicit parameters”, “dynamic variables”, “contextual values”).

To take a hypothetical, let’s use Twitter engineering as an example of a reasonably-sized org with a good mix of sophisticated and unsophisticated Scala users. What fraction of them would be familiar with the idea of an evidence as a way to pass parameters without specifying them? Consider that most Scala users, especially those not-yet-Scala-users who we would be hoping to attract to the language, would probably be less sophisticated than Twitter engineering. If those are the people we are trying to target, we should adjust our marketing accordingly.

3 Likes
#26

why not use something like inject ??

#27

Agree with @lihaoyi here – I don’t like the overloading of for.

Why not just make implicit something like val or def? We just declare a variable with a certain type, using the usual : symbol for type:

// mimics 'val'
implicit intMonoid: Monoid[Int] = ???

// mimics 'def'
implicit listOrder[T] given (T: Order[T]): Order[List[T]] = ???

or an anonymous one using the standard Scala notation _ for anonymity:

implicit _ : Context

I think that this approach makes the language more regular and orthogonal by creating a parallel between val / def / implied – it mixes well with the new given syntax (which I really like :grinning: ) and does not look too different from Scala 2 code.

2 Likes
#28

What’s the thesaurus entry for “context” – because that’s what you’re passing around in the end? Or something like a “where” clause, akin to generalized constraints in C#?

#29

It doesn’t matter what for means in english, whether the english meaning fits or not is irrelevant. for in Scala, and for in every other programming language, means something else entirely: looping or traversal over a collection (generalized to monad/functor in Scala).

Not quite: for is used in a very similar role in Rust. I wonder whether there was criticism for this? Do people working with Rust remember anything?

#30

I haven’t seen any complaints about overloading meaning of for keyword in Rust.

Generally reusing keywords sparingly can be OK if the keyword brings good intuitions. Relatively bad example of keyword reuse is static in context of inner class in Java. As a Java beginner I was wondering what was static about static classes and why outer classes didn’t need that keyword. Now static class looks acceptable, but Java tutorials failed to clear that up to me at the beginning.

#31

I like this idea but the drawback is that you would have to repeat the type in the right-hand side of the definition:

implicit _: Monoid[Int] = new Monoid[Int] { ... }

Whereas Martin’s proposal allows the following:

implicit for Monoid[Int] { ... }
#32

… and, please, no more uses of _ !

2 Likes
#33

The least astonishing syntax (for a Scala 2 programmer) would be:

implicit x extends Foo[Int] { ... }

This would look weird with anonymous instances implicit extends Foo[Int] { ... } but I don’t think anonymous instances are a good idea. Scala allows for multiple implementations of a single typeclass for a given type so e.g. pointing them out in error messages or correction suggestions (from compiler or IDE) would benefit from explicit names.

#34

How about of?

evidence intMonoid of Monoid[Int]
evidence of Monoid[Int]
etc...

It doesn’t work so well with implicit or implied, but I’m already sold on the argument that we shouldn’t stay too close to existing syntax as the similarities could then cause a lot of confusion in tutorials and example code.

2 Likes
#35

evidence of I like it.

#36

On the left hand side of an assignment, _ means that the name is not useful:

  val (x, _) = pair

So I believe that my usage of _ is not new – it is consistent with previous Scala grammar.
My proposal makes all term-level definition in Scala follow the same syntax, making the language simple:

{val|var|def|implicit} name : Type
2 Likes
#37

According to the dictionary, evidence of is wrong since it says that the thing after the “of” exists. I.e. evidence of Ord[Int] would effectively state “type Ord[Int] exists”. Well, we knew that!

Whereas evidence for means “in support of something”, so evidence for Ord[Int] would mean
there’s support for type Ord[Int]. It wouldn’t be too far fetched to say a value of type T is effectively the support for T.

I am preparing a third batch of files, this time with instance of. Then we can compare with that as well.

#38

I’ve played a bit with Dotty 0.14.0-RC1 and pushed my experiment here: https://github.com/julienrf/dotty-endpoints. In case that helps to see concrete code and not just specifications…

I exercised several features related to implicit parameters and definitions. I can write an equivalent program in Scala, to make the comparison easier, if needed. However, my program is surely not representative of all the usages of implicits.

Overall, I’m happy with the improvements brought by Dotty. More specifically:

  • the ability to define anonymous implicit values,
  • the reduction of boilerplate associated with implicit definitions,
  • the fact that extension methods are implicitly applicable,

The only things I’m not happy with are mostly syntactic: the for and implied keywords. But also, the fact that we can not define abstract implicit values is a problem for my use case.

As it was previously suggested in this discussion, I would be in favor of the following syntax:

// Named instance
implicit monoidInt extends Monoid[Int] {
  def identity = 0
  def (lhs: Int) combine (rhs: Int): Int = lhs + rhs
}

// Anonymous instance
implicit _ extends Monoid[Int] {
  // ...
}

// Derived instance
implicit _ [A] given (ma: Monoid[A]) extends Monoid[Option[A]] {
  // ...
}

// However, alias instances don’t work very well:
implicit _ extends Monoid[Int] = monoidInt
// Maybe we should replace “extends” with “of” everywhere?
implicit _ of Monoid[Int] = monoidInt

And I would appreciate support for abstract instances, with the following syntax:

// Abstract instance
trait Foo {
  type Bar
  // An abstract instance may not be anonymous
  implicit monoidBar extends Monoid[Bar]
}

trait FooImpl extends Foo {
  type Bar = Int
  implicit monoidBar = the[Monoid[Int]]
}
2 Likes
#39

While I agree in general, this is not another user of _. The same use that mentions the symbol is anonymous.

5 Likes
#40

I like your proposal – however I think that you can just use : for of and abstract cases:

// new named instance
implicit optionMonoid[A] given (s: Semigroup[A]) extends Monoid[A] {
  ...
}

// from somewhere else
implicit intMonoid: Monoid[Int] = ???

// abstract implicit
implicit monoid: Monoid[A]

2 Likes