Must admit to liking this one, it’s suitably declarative
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 = ...
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 = ...
In fact implied
acts as a given
anyway, you just have to remember when to use which. That feels unnecessary.
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).
How about:
introduce ListOrd as Ord[List[T]] given (ord: Ord[T])
I want a noun
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]
I quite like representative. Using it in sentences feels like natural, semantically correct English.
The representative Ordering for Ord[Int] is IntOrd.
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])
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?
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.