Function reusing in Julia without inheritance

If a function limits itself to Float64, it either

  • really only works on Float64 (because it manipulates them in a way that usually only works on Float64 specifically, like some bit-level manipulation) or
  • it’s limited unnaturally to Float64, even though it doesn’t require Float64 per se and rather depends on some other contract.

In the case you posted, square doesn’t actually care about Float64 at all - it only cares about having a pow method that takes as it’s first argument something and as its second argument an Integer. So in this case, square is erronously limited to Float64.

I’d solve this by

  1. not limiting my methods arbitrarily
  2. implementing ^ (and whatever else you may need, I think there are some packages that can do this kind of forwarding for wrapper types almost automatically)

Then, square(x) with typeof(x) === Var just works™. Moreover, because julia compiles specialized methods for each combination of input argument types, this will make your code much more composable and still keep performance. For example, not limiting your methods to ::Float64 would allow something like DifferentialEquations.jl to automatically take advantage of your code, by passing in a Dual number (which behaves like a float, i.e. has the same kinds of methods like ^ and * etc. implemented, but isn’t just a wrapper around Float64) to do automatic differentiation, enable execution tracing and a number of other things.

May I ask what kind of OOP you’re referring to? If you’re thinking Java-style where you’re inheriting not only behavior but also struct fields, you’re going to have a bad time trying to replicate it in julia. Method level type assertions on arguments should in my opinion only be used to distinguish different implementations in dispatch, not for limiting incoming arguments (there is no performance difference between a typed and untyped argument in a method, except in very, very rare cases you’re unlikely to run into).

3 Likes