Open LukaJCB opened 4 years ago
Also, I wrote a quick gist on how this could really help with future versions of cats-effect and bifunctor tagless final code: https://gist.github.com/LukaJCB/4440ad2bc351f1d8e98601f247ec4c02
Related/duplicate: https://github.com/lampepfl/dotty-feature-requests/issues/50
Polymorphic function types are a fantastic feature and without a doubt one of the features I'm most excited about in Dotty. They currently have a few restrictions, that hopefully we could relax in time for the full release of Scala 3.
Motivation
Sometimes when working with type classes of you run into issues where you e.g. want to say that a given type constructor can be compared for equality given that its contents are. A good example for this is the following issue in cats: https://github.com/typelevel/cats/issues/2308
Basically what we want to say that we can have an instance of
Eq[Cofree[S, A]
wheneverS[_]
itself also has anEq
instance for anyA
that in turn also has anEq
instance. One solution to this problem is to add a higher kinded version ofEq
. In Haskell this can be found asEq1
here (along withOrd1
,Show1
, etc.) https://hackage.haskell.org/package/transformers-0.4.2.0/docs/Data-Functor-Classes.htmlThe problem with this of course is that now, you start to have to duplicate most of the hierarchy in this fashion, even though they don't really convey any different meaning.
Ideally given polymorphic functions, we should be able to express
Eq1
as a polymorphic function type in the following way:In my opinion, this reads quite nicely as well, since it basically says an
Eq1
of a type constructor is anEq[F[A]]
for anyA
, givenEq[A]
.This problem props up in a bunch of different type classes as well, see for example Arbitrary1 or Monoid1.
Another example of something like this would be
SemigroupK
in cats, which is a universally quantified semigroup which would look like this:Furthermore there are some other really neat use cases that this would enable, including the ability to unite the bifunctor and monofunctor styles in a single type class hierarchy in a future version of
cats-effect
. (I will try to write more on this in a later post)Problems
As far as I can see there are two problems that hinder this from working well.
1. Polymorphic function types as implicit arguments don't behave like regular implicit params
Say we have the
Eq1
definition from above, my expectation would be that if I use a function with an abstractF[_]: Eq1
, that I should be able to useeqv
on anyF[A]
givenEq[A]
. Instead this is what happens:We can get it around this by calling the implicit manually, but it's not quite ergonomic:
Ideally it would pull in something like an
implicit def ev[A: Eq]: Eq[F[A]]
into scope to make it possible to just typex.eqv(y)
.2. Construction of these type aliases is fairly difficult (or impossible)
Ideally since something like
Eq1
is just a type alias, I wouldn't need to define anything extra. So if we had an instance like this:we should be able call the
foo
method from above withOption
:. This is what happens instead.I've tried to get around this by making
eqOption
non-implicit and defining an implicit instance ofEq1
instead, but could not get them to unify:Maybe there's something else you can do, but ideally it should just work :)
/cc @smarter