Alternative proposal for implicits

I actually had it wrong regarding generic type instances; they are indeed possible in Scala 2:

implicit def genericOrdering[A] = new Ordering[A] {  def compare(x: A, b: Y) = 0 } 

I assumed they don’t work as I couldn’t compile them previously due to ():

implicit def genericOrdering[A]() = ...

Talk about confusing syntax… anyway I’ve updated the proposal to reflect that.

Also, I would like to hear what any of you think about not allowing extensions of generics:

extension Ops[A] extends A

My main concern with this capability – which is available in Scala 2 – is that it allows to introduce type classes silently, without bounding them:

lens Eql {
  extension EqlOps[A] extends A {
    def ==[B]<Eql[A, B]>(b: B) = Eql.areEqual(this, b)
  }

  typeinstance IntEql implements Eql[Int, Int] { ... }
}

object App {
  1 == 2
}

lens App includes Eql

Useful, yes, but perhaps a bit too invisible and confusing?

If I’m understanding correctly, disallowing extension of generics would disallow postfix extensions like .some or .pure[F] from cats, which would be a deeply unpleasant hit to usability (particularly with the type inferencing issues around methods like foldLeft).

Is that a correct understanding?

1 Like

It is, and if you have more useful examples like that then please let me know. I would like to think about it for a while.

Sure, there are a bunch of them.

Most are, like .some, intended to defeat overly-specific inferred types. Cats has .asRight[L] and .asLeft[R] for Either, and equivalent .invalid[V] and .valid[I] for Validated.

Scalatest includes a bunch to help fail gracefully while removing various levels of nesting, mostly overrides of .value, but notably .futureValue unwinds a future with a configurable timeout and make dealing with them in tests much easier.

Ones like .pure[F] are less common, but I’ve got one defined for Convertible, and they mostly are used when you need to invoke something with two types and one of them is bound to a parameter - no point in specifying the type manually if it can be inferred by anchoring the call to the relevant parameter.

I’ve edited the proposal and added the ability for extensions to extend completely generic types, just like with the old extensions (implicit class). It seems to me that despite being a somewhat more complex feature – and hence more confusing – it is still very useful and cannot be easily replaced.

That means the “multiversal equality” is now supported by this proposal as well, and that type classes don’t require any special “infix notation syntax”.

I’m playing with the idea of adding a capability to include lenses of entire packages:

lens MyApp includes org.thirdparty.extensions._

I imagine this would be somewhat a crucial feature; otherwise, users would need to manually include many lenses; or alternatively, library authors would need to work hard assembling user friendly lenses.

This further stretches the line between lens and object, or rather between implicit resolution and name resolution (import). Perhaps simple implicit resolution deserve the same less verbose syntax as imports provide for name resolution:

import Foo
// short for:
import name Foo

interpret org.thirdparty.typeclasses._
// short for:
import lens org.thirdparty.typeclasses._

I still believe it is essential to separate between name resolution and implicit resolutions using a different “import” keyword for either case, and by grouping “named objects” under a different component (object) than implicit interpretations (lens).

Also, implicit conflict resolution still need to be added to the language, and I find it easier – syntactically speaking – to make it part of the modular component (lens) rather than the resolution construct (interpret), but like with any syntax-related decision there is a lot of experimentation that needs to be done to assert which way is better.