Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Make extension methods in typeclasses less surprising #137

Open
ysthakur opened this issue Sep 5, 2020 · 0 comments
Open

Make extension methods in typeclasses less surprising #137

ysthakur opened this issue Sep 5, 2020 · 0 comments

Comments

@ysthakur
Copy link

ysthakur commented Sep 5, 2020

Currently, the Dotty documentation shows this example of implementing semigroups and monoids.

trait SemiGroup[T]:
  extension (x: T) def combine (y: T): T

trait Monoid[T] extends SemiGroup[T]:
  def unit: T

And shows this example of using it:

def combineAll[T: Monoid](xs: List[T]): T =
    xs.foldLeft(summon[Monoid[T]].unit)(_.combine(_))

This seems a bit surprising to me - the extension method combine magically came into scope when there was a Monoid[T] in scope, but the method unit still has to be invoked explicitly on the Monoid instance. It would make more sense if one had to do import summon[Monoid[T]]._ before being able to use combine that way. But that's annoying, so it would be even nicer if one could let the compiler know that Monoid and SemiGroup were typeclasses and make it so that whenever there an instance of Monoid is in scope, its members are also in scope. I was thinking something like this:

@typeclass
trait SemiGroup[T]:
  extension (x: T) def combine (y: T): T

@typeclass
trait Monoid[T] extends SemiGroup[T]:
  def unit: T

def combineAll[T: Monoid](xs: List[T]): T =
    xs.foldLeft(unit)(_.combine(_))

Despite perhaps complicating the language more, it feels more uniform - no special treatment for extension - and looks a little more like Haskell's typeclasses (not that that's something Dotty should necessarily aim for, but it's satisfying).

And of course, one would still be able to use summon[Monoid[T]].unit if there happened to be another unit method in scope.

The typeclass annotation could also generate an apply method in the companion object for convenience, as was suggested in this comment. So if there happened to be unit methods in scope, one would be able to do xs.foldLeft(Monoid[T].unit)(_.combine(_)) and avoid this boilerplate:

object Monoid:
  def apply[T: Monoid] = summon[Monoid[T]]
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant