Currently Cats is using capital letters for type class instance parameter names, such as this:
def map[B](f: A => B)(implicit F: Functor[F]): F[B]
It may be worth noting that this same convention is used by Scalaz.
This convention is fairly straightforward once you are used to it, and it is very concise. However, both @stew and I have seen people struggle with confusion with this convention.
I believe this confusion tends to stem from the fact that parameter names are typically not capitalized in Scala, and they are either thrown off by F being both a value and a type, or they don't realize that it is a value in addition to a type within the method.
Since one of the goals of Cats is to be approachable and fairly beginner-friendly, I'm wondering if we should adopt a new convention for type class instance parameter names.
What do people think of something like this?
def map[B](f: A => B)(implicit functorF: Functor[F]): F[B]
It adds a bit of verbosity. However, I think that to many people it may convey its meaning more clearly.
See also this gitter conversation in which @propensive weighs in with some thoughts.
Hmm. My gut reaction to functorF is pretty negative. I think there are two things I don't like:
A great question is what alternative would I propose? I don't have a clear answer yet.
If everyone else wants to move to functorF-style parameters I won't stand in the way. But I am holding out hope that we can come up with something better.
One question for @ceedubs and @stew:
Are people confused by seeing params like F and G used? Or is just the existence of the parameter that is confusing?
If it is the former, then trying to make it easier to avoid needing to reference the implicit params would be useful (and we could move to context bounds in many-but-not-all cases). If the latter, then trying to move to context bounds might help, but we still need to solve the underlying problem.
that we are using F to mean one thing in contexts and another in value contexts.
I see. So it mirrors (but is possibly worse than) the confusion between a type Foo and its companion object Foo.
In defense of the status quo, it simplifies refactoring whenever a context bound is found to be too strict or too lax. I'm open to change here, but functorF has a faint odor of Hungarian notation.
A problem with the A-convention is that it doesn't scale:
def foo[A](implicit ev0: Foo[A], ev1: Bar[A]) = ...
Can't be written using A for both parameters. In these cases I have punted and used F and B, but now I'm thinking maybe a middle ground like evF and evB would work. Lowercase makes the identifiers less jarring to see in term position, but they're still weird enough that you understand they're somehow distinct from normal parameters.
I'm really not a fan of Scalaz's single upper case term name convention.
In @tpolecat's example I would most likely use fooA and barA or possibly abbreviate to fA and bA. In the case of functors I would most likely abbreviate functorT to fT.
@ceedubs @tpolecat
Doing some issue archeology. Considering that the F-convention has scaped to other libraries (such as cats-effect or http4s), and that there is even a compiler plugin for generating that F name... Is this something you stil are interested to push?
We've spent 5 years building learning materials and precedent around this convention, which I think only raises the bar to a change that didn't gather critical mass 5 years ago. I like questioning our old decisions, but I think at this point, a change here is as likely to confuse as to assist.
Most helpful comment
We've spent 5 years building learning materials and precedent around this convention, which I think only raises the bar to a change that didn't gather critical mass 5 years ago. I like questioning our old decisions, but I think at this point, a change here is as likely to confuse as to assist.