Determining the variance of the function type 

In this section, we will attempt to understand how to reason about function types. While Julia does not provide too much help in formalizing function types, it does not stop us from doing the analysis ourselves. In some strongly typed, static OOP languages, function types are more formally defined as the combination of method arguments and return type.

Suppose that a function takes three arguments and returns a single value. Then we can describe the function with the following notation:

Let's continue the animal kingdom example and define some new variables and functions, as follows:

female_dogs = [Dog("Pinky"), Dog("Pinny"), Dog("Moonie")]
female_cats = [Cat("Minnie"), Cat("Queenie"), Cat("Kittie")]

select(::Type{Dog}) = rand(female_dogs)
select(::Type{Cat}) = rand(female_cats)

Here, we have defined two arrays—one for female dogs and another for female cats. The select function can be used to randomly select a dog or cat. Next, let's consider the following function:

match(m::Mammal) = select(typeof(m))

The match function takes a Mammal and returns an object of the same type. Here's how it works:

Given that the match function can only return Dog or Cat, we can reason the function type as follows:

Suppose that we define two more functions, as follows:

# It's ok to kiss mammals :-)
kiss(m::Mammal) = "$m kissed!"

# Meet a partner
function meet_partner(finder::Function, self::Mammal)
partner = finder(self)
kiss(partner)
end

The meet_partner function takes a finder function as the first argument. Then, it calls the finder function to find a partner and finally kiss the partner. By design, we are going to pass the match function that we defined in the preceding code. Let's see how it works:

So far, so good. From the perspective of the meet_partner function, it expects the finder function to accept a Mammal argument and returns a Mammal object. That is exactly how the match function was designed. Now, let's see if we can mess it up by defining a function that does not return a mammal:

neighbor(m::Mammal) = Crocodile("Solomon")

Although the neighbor function can take a mammal as an argument, it returns a crocodile, which is a reptile rather than a mammal. If we try to pass it to the meet_partner function, we are met with disaster:

What we have just proven is quite intuitive. As the return type of the finder function is expected to be a Mammal, any other finder function that returns any subtype of Mammal would also work. So the return type of function types is covariant. 

Now, what about the arguments of function types? Again, the meet_partner function is expected to pass any mammal to the finder function. The finder function must be able to accept either a dog or cat object. It would not work if the finder function only takes a cat or dog. Let's see what happens if we have a more restrictive finder function:

buddy(cat::Cat) = rand([Dog("Astro"), Dog("Goofy"), Cat("Lucifer")])

Here, the buddy function takes a cat and returns a mammal. If we passed it to the meet_partner function, then it would not work when we want to find a partner for our dog Chef:

So the arguments of function types are not covariant. Could it be contravariant? Well, to be contravariant, the finder function must accept a supertype of Mammal. In our animal kingdom, the only supertype is Vertebrate; however, Vertebrate is an abstract type and it cannot be constructed. If we instantiate any other concrete type that is a subtype of Vertebrate, it would not be a mammal (otherwise, it would be considered a mammal already). Therefore, function arguments are invariant.

Stated more formally, this looks as follows:

Function g is a subtype of function f, as long as T is Mammal and S is a subtype of Mammal. There is a saying about this: "Be liberal in what you accept and conservative in what you produce."

While it is fun doing this kind of analysis, do we gain anything, given that the Julia runtime does not support function types as granular as those we have seen? It seems to be possible to simulate a type-checking effect on our own, which is the topic of the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.31.73