Working with parametric composite types

When designing composite types, we should assign each field a type. Oftentimes, we don't really care exactly what those types are, as long as the type provides the functionality that we want. 

A classic example would be numeric types. The concept of numbers is simple: basically the same as we were taught in elementary school. In practice, many numeric types are implemented in computer systems because of the different physical storage and representations of data. 

By default, Julia ships with the following numeric types; concrete types are darker:

Do you remember when we designed a composite type to represent a stock in an investment portfolio earlier in this chapter? Let's revisit that example here:

struct Stock <: Equity
symbol::String
name::String
end

If I have to hold some stocks in my brokerage account, then I should also keep track of the number of shares that I own. To do this, I can define a new type called StockHolding, as follows:

struct StockHolding
stock::Stock
quantity::Int
end

The Int data type is by default aliased to either Int64 or Int32, depending on whether you are using the 64-bit or 32-bit version of Julia. This seems reasonable just to get started, but what if we need to support fractional shares for a different use case? In that case, we can just change the type of quantity to Float64:

struct StockHolding
stock::Stock
quantity::Float64
end

We basically widen the type of quantity field to a type that supports both integer and floating-point values. It may be a reasonable approach, but if we need to support both Int and Float64 types, then we would have to maintain two slightly different types. Sadly, if we do create two different types, then it becomes a maintenance nightmare.

To make it more flexible, we can redesign the StockHolding type with a parameter:

struct StockHolding{T} 
stock::Stock
quantity::T
end

The symbol T inside the curly braces is called a type parameter. It serves as a placeholder that can be used as a type in any of the fields.

Now, we have the best of both worlds. The StockHolding{Int} type refers to the type that contains a quantity field of the Int type. Likewise, the StockHolding{Float64} refers to the type that contains a quantity field of the Float64 type

In practice, the T type parameter can only be a numeric type, so we could further qualify T as any subtype of Real:

struct StockHolding{T <: Real} 
stock::Stock
quantity::T
end

Here's how we read this—the StockHolding type contains a stock and a quantity of the T type that is a subtype of Real. The second part of the sentence is important; it means that we can create a new StockHolding with the type of quantity as Float16, Float32, Float64, Int8, Int16, Int32, and so on.

Let's try instantiating the StockHolding object with different kinds of type parameter, such as Int, Float64, and Rational:

We can see that different StockHolding{T} types are created automatically according to the argument that was passed to the constructor.

Another use of parametric types is to enforce the consistency of field types. Suppose that we want to design another kind of stock-holding object to track the price and market value of the holding. Let's call it StockHolding2 to avoid confusion with the preceding one. The following is what it looks like:

struct StockHolding2{T <: Real, P <: AbstractFloat} 
stock::Stock
quantity::T
price::P
marketvalue::P
end

Knowing that the type for quantity may not be the same as the type for price and marketvalue, we have added a new type parameter, P. Now, we can instantiate a StockHolding2 object that contains an integer quantity while having floating-point values for price and market value fields:

Note that the type is StockHolding2{Int64, Float64}, as shown in the preceding screenshot. In this case, the type parameter T is Int64 and the parameter P is Float64

As we declared that both the price and marketvalue fields must be of the same type, P, does Julia enforce this rule for us? Let's give it a try:

Yes, it does! We correctly received an error because we passed a Float64 value for price, but an Int64 for marketvalue. Let's take a closer look at the error message, which revealed what the system expects. The closest candidate function for StockHolding2 takes a P type for the third and fourth arguments, where P is any subtype of AbstractFloat. Because In64 is not a subtype of AbstractFloat, there is no match and so an error was thrown. 

Parametric types can be abstract as well. We will go over this next.

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

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