We continue our exploration of Toccata types by looking at the link between
It's not enough to just put data together in custom types. We also need to write functions that do things to or with that data. Now, it would be possible to just write a 'plain' function that would take it's arguments and explicitly test the types of the values given to determine what to do. But there's a better way. It goes by multiple names; Parametric Polymorphism, Polymorphic Dispatch, or what have you. The idea is, at a call site, the compiler inserts code that checks the types of the argument(s) and chooses which function to execute based on the types given. This is what Toccata does. So all that remains is to tell the compiler how to distinguish between calls to 'plain' functions and calls to functions that need to be dispatched by type. That is where
defprotocol comes in. It lets the programmer provide a list of functions that are to be dispatched at runtime. I wrote about Toccata's core protocols here. Toccata only chooses which function to execute based on the type of the first argument. The types of the other arguments don't factor into that decision.
So let's take one of the core protocols and look at it a little closer. Hmmmm, I pick this one.
Looking at the
nth function, we see two type assertions. The first one is like we've seen before and asserts that the parameter
n is always an integer. You should never put a type assertion on the first parameter to a protocol function, because it is the dispatch type.
The next thing we see is the
assert-result expression. This expression includes a symbol, in this case
a. This is only used inside the assertion expression and stands in for the value returned by the function.
These two assertions give us the contract for all the
nth functions. They take a collection of unknown type and an integer parameter
n. And they all return a
Maybe value, which may be
nothing or may contain the nth value of the collection. Now, if anyone tries to implement an
nth function for a collection that doesn't return a Maybe value, they're going to get an error at compile time if the comipiler can determine that to be the case. Otherwise, the compiler will insert some code in that particular implementation to check the return value. Regardless, the compiler will know that any call site that calls
nth will evaluate to a Maybe value. Pretty neat, huh?
Now, let's look at a type that wraps Vector for some reason.
This is a nonsense type, but does show how to implement a protocol function. And since
v is guaranteed to always be a Vector, the compiler can 'pre-dispatch' the call to
nth in the implementation. But how is
nth defined for Vector? That is here.
Vector is defined in the sub-basement of Toccata, so there's no way to write a
deftype expression for it. Also, if you read through
core.toc, you'll see the functionality for some of the core types has to be broken into several pieces.
More importantly, you can write an
extend-type to add protocol functions to any type you want. The only difference is that in
deftype expressions, you can access field values by name. Whereas in
extend-type expressions, you have to use field accessors on the dispatch parameter.