People have been asking for interfacing pattern in Julia, but we do things through multiple dispatch. Here’s how.
Imagine you want an interface for an “ordered” type.
ordered_interface(x) = ordered_interface(typeof(x))
ordered_interface(::Type) = error(“This type not sortable”) # can give more information here.
ordered_interface(::Type{<:Number}) = nothing.
And so on…
Then, when you want to use it, use…
sort!(x) = ordered_interface(eltype(x)); main_sorting_algorithm!(x)
This example can be applied in general.
2 Likes
I think the trouble here is that you can’t write a generic library based on this pattern because you depend on users to define ordered_interface
for their type. That’s why people trying to solve interface problems are generally setting up a set of tests for what methods are implemented and that those methods return the expected thing.
This is basically Tim Holy Traits if I’m understanding your pattern correctly, which is an awesome strategy for organizing your own code, but hasn’t solved Julia’s composability because of the problem above.
3 Likes
I do not understand your criticism. Third-parties always have to define the interface functions for their type, even if the language has some native syntax/utilities for the interface pattern.
Sure, but in the example above, you could have just defined the interface function sort!
or main_sorting_algorithm!
. The trait tester doesn’t add any additional information to the situation. Further, you could wrongly mark a type as applicable here, and violate assumptions accidentally.
That’s why PropCheck
and kin are checking if methods exist and, if so, if some set of inputs produces expected results. This is more like a static traits/interface system where you are forced to make sure you define all the necessary interface functions and at least return the correct type for each of those.
Well, multiple dispatch often have fine-grained requirements like… if this is of type A, then method B is required, and so on. Interface-checking can run lots of logics at the very least. Multiple dispatch can fix that by providing the proper error message to it. But no library author can be forced to use a specific pattern because multiple dispatch is fine-grained. Interfacing like those found in Rust won’t cut it. With holy trait pattern and inheritance, you can do things like creating a hierarchy of traits and so on.
And statically typed sub language would likely be a duck-typed language as well, like C++ templates.
IMHO, one useful feature would be an analysis tool that can check if whatever throws an error in advance. Then the user can filter out possible value errors and get only type-based errors shown.