Thursday, August 24, 2017

Functions in Scala

In addition to being functional, Scala is also object-oriented. Hence a function is also an object and can be used wherever a value can be used.

scala> val isEven = (n: Int) => n % 2 == 0
isEven: Int => Boolean = $$Lambda$1002/925024581@4cbc2e3b

scala> isEven(1)
res0: Boolean = false

scala> isEven(2)
res1: Boolean = true

Behind the scenes, each call to isEven(<n>) is being converted to isEven.apply(<n>)

Actually, the isEven function is of type trait Function1[-T, +R] where T = Int and R = Boolean.

Every function in Scala maps to one of the FunctionN traits where N = 0 to 22. Yes, there are only 23 of them which implies that the number of function arguments cannot exceed 22.

Now, why do we need these FunctionN trait definitions in first place?

As Scala allows functions to be defined outside of classes/objects and as Scala is statically typed and is OO implying that functions are objects as well, it becomes essential that functions have to a type. Hence the need for these FunctionN traits.

Also note that the FunctionN types are contravariant w.r.t arguments (T1, T2 etc.) and covariant w.r.t return types (R). This is ensure flexibility so as to conform to the subtyping rule that subtypes should be permissive in what they accept and restrictive in what they produce. For more about this, refer to this nice reddit post by balefrost.







Wednesday, August 16, 2017

Covariance & Contravariance in Java

Covariance

If the generic type's subtype relationship is the same as the type parameter T, then the generic type is covariant w.r.t type parameter T.

A is a subtype of B => Generic<A> is a subtype of Generic<B>

In order to define a generic type as covariant, the extends keyword is used which defines the upper bound.

Example:

public class Animal {}
public class Mammal extends Animal {}
public class Cat extends Mammal {}
public class Dog extends Mammal {}


List<Animal> animals = ...
List<Mammal> mammals = ...
List<Cat> cats = ...
List<Dog> dogs = ...

public void someFunc(List<? extends Mammal> mammals)
                                |
                                V
               Here 'extends' defines the upper-bound as Mammal

Covariants are producers 

New items cannot be added to covariants after their construction.
public void someFunc(List<? extends Mammal> mammals) {
    mammals.add(new Cat());  ==> Does not compile
    mammals.add(new Mammal()); ==> Does not compile
}
   
However, one can access the items from covariant types, like so...

public void someFunc(List<? extends Mammal> mammals) {
    Mammal m = mammals.get(0); ==> Compiles
}

Also, someFunc() can only accept List<Mammal> or it's subtypes. 

someFunc(cats); ==> Compiles
someFunc(mammals); ==> Compiles
someFunc(animals); => Does NOT compile


Contravariance

If the generic type's subtype relationship is the opposite of type parameter T, then the generic type is contravariant w.r.t type parameter T.

A is a subtype of B => Generic<B> is a subtype of Generic<A>

In order to define a generic type as contravariant, the super keyword is used which defines the lower bound.

Example:

public void someFunc(List<? super Mammal> animals)
                                |
                                V
               Here 'super' defines the lower-bound as Mammal

Contravariants are consumers 

New items can be added to contravariants after their construction.
public void someFunc(List<? super Mammal> animals) {
    animals.add(new Cat());  ==> Compiles 
                             ==> Adding a Cat compiles as the 
                                 lower bound does NOT apply to the 
                                 constituent type <?> but only to                                    the generic type List<?>
    animals.add(new Mammal()); ==> Compiles
}

However, one cannot access the items from contravariant types, like so...

public void someFunc(List<? super Mammal> animals) {
    Animal a = animals.get(0); ==> Does not compile
}

Also, someFunc() can only accept List<Mammal> or it's super types. 

someFunc(cats); => Does NOT compile
someFunc(dogs); => Does NOT compile
someFunc(mammals); ==> Compiles
someFunc(animals); ==> Compiles
someFunc(objects); ==> Compiles