Covariance can be eschewed with map: (A => B) => F[A] => F[B]
Contravariance can be eschewed with contramap: (A => B) => F[B] => F[A]
Sure, there might be allocation costs, but leaving this aside, I am most
interested in the implications for the type system and how it plays out
in practice. It seems I can appeal to existing literature on type
theory, but it falls far short of answering the question, "how to best
use Scala with regard to variance?" It is this huge gap that I would
love to fill in. Should we say that Scala has two type systems (variance
annotation/inheritance vs map/contramap) that do not unify? Let's not
even get started on the evaluation models (call-by-name, etc) here with
regard to unification.
Some say, yes definitely, use it. Others say no, definitely do not use
it. In both cases, I can construct a rebuttal that I do not know the
answer for.
IO is declared invariant. You have IO[A] and "A inherits from B" and you
want IO[B]. If we get rid of inheritance and where it is instead
modelled as =>, then you'd have IO[A] and "A => B" and you want IO[B].
Easy peasey: map. Yeah it's three letters for what would otherwise be
implicit (shame the . keyword is taken eh?), but why should map get
special treatment with regard to even having language keywords
(extends/with) anyway?
Existing Scala libraries have both variance/inheritance annotation and
map. But even ignoring the built-in libraries (which I find almost
always need rewriting when it comes to practical application for other
reasons anyway), what would be the approach if you were to start from
scratch? Would you use variance annotations, map/contramap, both,
something else?
It boggles my mind. So many questions. Comments most appreciated.