Implementing our own function type dispatch

As we have seen earlier in this section, Julia creates a unique function type for every function, and they are all subtypes of the Function abstract type. We seem to be missing an opportunity for multiple dispatch. Taking the all function from Base as an example, it would be very nice if we could design a type that represents predicate functions rather than letting all fail miserably when an incompatible function is passed.

In order to work around this limitation, let's define a parametric type called PredicateFunction as follows:

struct PredicateFunction{T,S}
f::Function
end

The PredicateFunction parametric type just wraps a function f. The type parameters T and S are used to represent the types of function arguments and return a type of f respectively. As an example, the iseven function can be wrapped as follows, because we know the function can take a number and return a Boolean value:

PredicateFunction{Number,Bool}(iseven)

Conveniently, since Julia supports callable structs, we can make it so that the PredicateFunction struct can be invoked as if it was a function itself. To enable this, we can define the following function:

(pred::PredicateFunction{T,S})(x::T; kwargs...) where {T,S} = 
pred.f(x; kwargs...)

As you can see, this function merely forwards the call to the pred.f wrapped function. Once it is defined, we can do some small experiments to see how it works:

That looks pretty good. Let's define our own safe version of the all function as follows:

function safe_all(pred::PredicateFunction{T,S}, a::AbstractArray) where 
{T <: Any, S <: Bool}
all(pred, a)
end

The safe_all function takes a PredicteFunction{T,S} as the first argument, with the constraint that T is a subtype of Any and S is a subtype of Bool. It's exactly the function type signature we want for predicate functions. Knowing that Number <: Any and Bool <: Bool, we can definitely pass the iseven function to safe_all. Let's test it now:

Bravo! We have created a safe version of the all function. The first argument must be a predicate function that takes anything and returns a Boolean value. Rather than taking a generic Function argument, we can now enforce strict type matching and participate in multiple dispatch.

That is enough about variance. Next, we will move on and revisit the rules for the parametric method dispatch.

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

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