The collections library does backflips to accommodate subtyping: when you use map
on a specific collection type (list, map, etc.), you'll (usually) get the same type back. It manages this through the use of an extremely complex inheritance hierarchy together with type classes like CanBuildFrom
. It gets the job done (at least arguably), but the complexity doesn't feel very principled. It's a mess. Lots of people hate it.
The complexity is generally pretty easy to avoid as a library user, but for a library designer it's a nightmare. If I provide a monad instance for Seq
, that means all of my users' types get bumped up the hierarchy to Seq
every type they use a monadic operation.
Scalaz folks tend not to like subtyping very much, anyway, so for the most part Scalaz stays around the leaves of the hierarchy—List
, Vector
, etc. You can see some discussion of this decision on the mailing list, for example.
When I first started using Scalaz I wrote a lot of utility code that tried to provide instances for Seq
, etc. and make them usable with CanBuildFrom
. Then I stopped, and now I tend to follow Scalaz in only ever using List
, Vector
, Map
, and Set
in my own code. If you're committed to "Scalaz style", you should do that as well (or even adopt Scalaz's own IList
, ISet
, ==>>
, etc.). You're not going to find clear agreement on best practices more generally, though, and both approaches can be made to work, so you'll just need to experiment to find which you prefer.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…