Reference: http://hackage.haskell.org/package/contravariant-1.5/docs/Data-Functor-Contravariant.html
Reference: https://ocharles.org.uk/blog/guest-posts/2013-12-21-24-days-of-hackage-contravariant.html
Reference: https://bartoszmilewski.com/2015/02/03/functoriality/
Referring my other github projects could be helpful:
- Functor's basics: https://github.com/mtumilowicz/java11-category-theory-optional-is-not-functor
 - Functor related to 
Op: https://github.com/mtumilowicz/java11-category-theory-reader-functor 
class Functor f where
  map :: (a -> b) -> f a -> f b
- Functor is a sort of "producer of output": if 
fis a functor, thenf arepresents a "producer" of typea - Functor can have its type adapted
 - Using a function 
a->bwe can modify output to typeb(usingmap) - Three easy examples of functors (and modification of output):
Option[A]: usingmapwe can adapt potentiallyNone[A]toNone[B]List[A]: usingmapwe can adaptList[A]toList[B]Reader[R, A](r -> a) - we can think about it as a functions takingras input and producinga; usingmapwe can convert output tob
 
map allows us to change the output, but we are unable to change the input - and that's where contravariance comes in
class Contravariant f where
  contramap :: (b -> a) -> f a -> f b
- For contravariant 
f,f arepresents input - Using a function 
b -> awe can modify input to typeb(usingcontramap) - Notice that a contravariant functor is just a regular functor from the opposite category (category with reversed morphisms)
 
We provide example of contravariant functor with tests.
- haskell is more expressive:
type Op r a = a -> r instance Contravariant (Op r) where -- (b -> a) -> Op r a -> Op r b contramap f g = g . f - and the Scala code comes here:
trait Op[R, A] extends (A => R) { def map[B](f: B => A): Op[R, B] = this.compose(f).apply }- Notice that the function f inserts itself before
the contents of 
Op- the functiong. 
 - Notice that the function f inserts itself before
the contents of 
 - tests:
- we have function counting list's size, and we want to modify
it to accept 
Set:val sizer: Op[Int, List[String]] = _.size sizer.map((set: Set[String]) => set.toList).apply(Set()) should be(0) sizer.map((set: Set[String]) => set.toList).apply(Set("a")) should be(1) sizer.map((set: Set[String]) => set.toList).apply(Set("a", "b", "c")) should be(3) - we have predicate to check if a given int is even, and
we want to transform it to accept ints given as a string
val isEven: Op[Boolean, Int] = _ % 2 == 0 isEven.contramap((s: String) => s.toInt).apply("2") should be(true) isEven.contramap((s: String) => s.toInt).apply("3") should be(false) 
 - we have function counting list's size, and we want to modify
it to accept