cats
cats copied to clipboard
Implicit resolution fails for newtypes with shapeless `Lazy`
Several types in cats such as NonEmptyChain are defined using the newts / newtypes approach: the advantage of this is that there is zero (runtime) overhead to using these types versus the possibly-empty equivalents; however, it leads to several issues, raised here and elsewhere:
- https://github.com/typelevel/cats/issues/3117
- https://github.com/typelevel/cats/issues/2928
- https://github.com/typelevel/cats/issues/2582
To this list I would like to add an issue with implicit resolution when using shapeless Lazy: see https://github.com/milessabin/shapeless/issues/942 for the gory details but, in short, it's impossible to write something like:
def foo(implicit tc: Lazy[Semigroup[NonEmptyChain]]): ...
The non-Lazy version works just fine (as do by-name implicits in scala 2.13+). Interestingly, the Lazy version does work if either (or both) of the following are true:
- the implementation class (
NonEmptyChainImplin this case) is public - the required implicits are explicitly re-imported at the call site (
import NonEmptyChain._)
Clearly the end-user can't control the first, and the second is far from ideal (and took several days to discover as a workaround).
It seems like a lot of these issues boil down to variations on https://github.com/scala/bug/issues/6794 and, given that this is unlikely to be fixed in its entirety any time soon, is there some benefit to considering moving away from newtype altogether?