Proposal to Add Implied Instances to the Language

Must admit to liking this one, it’s suitably declarative

4 Likes

implicit as a noun reads quite well if for goes before given, including anonymous definitions. And it obviously feels much more familiar.

implicit OptionMonoid[T] for Monoid[Option[T]] given Monoid[T] = ... 

implicit OptionMonoid[T] for Monoid[Option[T]] given (monoidT: Monoid[T]) = ... 

implicit [T] for Monoid[Option[T]] given Monoid[T] = ... 

implicit global for ExecutionContext = ...

implicit for ExecutionContext = ...
4 Likes

Let’s go full circle and change implied to given:

given OptionMonoid[T]: Monoid[Option[T]] given Monoid[T] = ... 

given OptionMonoid[T]: Monoid[Option[T]] given (monoidT: Monoid[T]) = ... 

given _[T]: Monoid[Option[T]] given Monoid[T] = ... 

given global: ExecutionContext = ...

given _: ExecutionContext = ...

:smiley:

In fact implied acts as a given anyway, you just have to remember when to use which. That feels unnecessary.

1 Like

Taking a step back…

The fact that we use implied, implicit or even given for these things points to an open question: If Scala’s implicit system is fundamentally term inference, what is a good name for the term that is inferred for a type? We say “canonical instance”, or “implicit instance”, or “implied instance”, but can’t we find something crisper? Something that is a noun by itself?

  • evidence works if we identify types with properties, but not everyone is comfortable with that.
  • instance is too generic by itself.

One word that would work is representative. Term inference constructs the representative of a given type. When I define IntOrd, that’s then a representative for the type Ord[Int].

The terminology around representative also seems to make sense. One can say, a type is represented (i.e. there is an an implicit value of that type), or that a representative is eligible in some context.

To explore this further, I have made yet another copy of the doc pages (overview, definitions) based on the new terminology. It uses repr as a keyword, since representative is too long.

Conclusions that I have taken so far from this thread:

  • The basic structure of having one syntax construct to define representatives (or whatever) works well.
  • But given should come last
  • And given in expressions should be used with method call syntax instead of infix.

The main contenders for implicit definitions right now is to use either repr or implicit as a noun. One can form an opinion how everything hangs together by reading carefully the two variants of the doc pages. For implicit as a noun, these are (overview, definitions).

2 Likes

How about:

introduce ListOrd as Ord[List[T]] given (ord: Ord[T])

I want a noun :wink:

I think repr usually stands for representation, not representative. For example see Python’s use of repr and even similar usage in Haskell.

Moreover, a representative is “an example of a class or group”; representatives have the same kind as the things they represent. So a representative of a set of elements would be one element of the set, and a representative of a type would be a value of that same type, not of another type. For example, the union-find data structure has a concept of a representative node, which is used to represent all nodes whose element values belong to a certain subset.

Therefore I think the “representative” terminology is inadequate, in addition to being too verbose and its shorthand form repr too ambiguous.

Personally I strongly support the use of implicit, but without the strange novel syntax. While using for or of may be marginally more aesthetically pleasing when defining implicit instances, it also makes the connection to what they desugar to unnecessarily obfuscated.

Granted, one should focus on the intent and not the mechanism, but surely the following syntax expresses intent just as well, while also being clear about what it does behind the scenes:

implicit IntOrd extends Ord[Int] {
  def compare(x: Int, y: Int) = ...
}
implicit ListOrd[T].given(ord: Ord[T]) extends Ord[List[T]] {
  def compare(xs: List[T], ys: List[T]): Int = ...
}

// stands for:

implicit object IntOrd extends Ord[Int] {
  def compare(x: Int, y: Int) = ...
}
class ListOrd[T].given(ord: Ord[T]) extends Ord[List[T]] {
  def compare(xs: List[T], ys: List[T]): Int = ...
}
final implicit def ListOrd[T].given(ord: Ord[T]): ListOrd[T] =
  new ListOrd[T]

Anonymous instances:

implicit _ extends Ord[Int] {
  def compare(x: Int, y: Int) = ...
}

Abstract instances:

implicit IntOrd: Ord[Int]
2 Likes

I quite like representative. Using it in sentences feels like natural, semantically correct English.

The representative Ordering for Ord[Int] is IntOrd.

2 Likes

I often see Repr as a name of generic parameter in Scala collections. Having a repr keyword next to that would be somewhat confusing.

Don’t forget about the ambiguity coming from optional = operator, as I’ve shown here Proposal to Add Implied Instances to the Language - #95 by tarsa :

abstract class ImplicitsAggregate {
  // this does work like a class or object definition with refinement
  implied AbcdXyz for Abcd[Xyz] { .. }

  // this does work like a val or def
  implied EfghXyz for Efgh[Xyz] = { ... }

  // what is that? abstract val or def or maybe a class or object definition?
  implied IjklXyz for Ijkl[Xyz]
}

So a representative of a set of elements would be one element of the set, and a representative of a type would be a value of that same type.

Exactly. But that’s precisely what is meant in the proposal! IntOrd a representative of Int[Ord] and a value of the same type.

It’s an instance definition. Same as object foo extends Bar is an instance definition. It’s important that we use a keyword as a connective to make that clear. A : would indeed be ambiguous.

canon ListOrd of Ord[List[T]] given (ord: Ord[T])

1 Like

Okay, but a representative element is supposed to represent the other elements in one way or another. In union-find data structures, the representative node represents other nodes on the property “belongs to a certain subset”.

In what way does IntOrd represent instances of Int[Ord]? Does IntOrd represent all possible orderings of integers?

1 Like

So implementing abstract implicit instances would look like that:

abstract class BunchOfTypeclasses {
  implied AbcXyz: Abc[Xyz] given Efg[Xyz] // abstract one
}
class ConcreteTypeclasses extends BunchOfTypeclasses {
  implied AbcXyz for Abc[Xyz] given Efg[Xyz] { ... } // concrete one
}

It looks somewhat irregular to change : to for. Is there anything similar in current Scala?

That’s a shame… because I thought that specify might work quite well

canon ListOrd of Ord[List[T]] ...
I like this one

Trust you to bring out the big guns…

I quite like representative/repr.

Another short alternative could be “aide”.

I also agree that a noun describing what purpose it is serving is better than something that describes one its properties.

E.g. I think that “val” and “var” and more informative than say “def”, because “def” doesn’t say what is being defined.

I’m a Scala 2 user who hasn’t followed the Dotty discussions, only this one. Could you please explain why there’s a new form equivalent to implicit def but not one equivalent to implicit val or implicit lazy val?

A val is useful when invoking the def has some cost, e.g. it allocates and returns many new objects. There are other reasons which might be ruled out as antipatterns (e.g. the def returns non-interchangeable values over time).

You can always use implicit instances aliases that forward to vals, var or lazy vals:

implicit AbcXyz: Abc[Xyz] given whatever: Whatever = somethingNonImplicit

def somethingNonImplicit given whatever: Whatever: Abc[Xyz] = ???
implicit AbcXyz: Abc[Xyz] = somethingNonImplicit

/* lazy */ val somethingNonImplicit: Abc[Xyz] = ???

I think that val would be used automatically whenever possible underneath implicit, but I haven’t seen any statement on that.