# Function reusing in Julia without inheritance

Suppose I have

``````struct Var
x::Float64
end
``````

and

``````function square(x::Float64)
return x^2
end
``````

To define a function for squaring a Var and returning a Var, I could write

``````function square(var::Var)
y = square(var.x)
return Var(y)
end
``````

However, if I have many other functions like square (cube, square_root, etc), how can I avoid rewriting the function for every function that uses Float64 as an input? I could think of a way in an OOP language by inheriting from an abstract class with undefined function, but not sure how to do this in Julia.

1 Like

In a rather similar way - define an abstract supertype.

``````abstract type MyVars end

struct Var <: MyVars
x:: Float64
end

square(var:: MyVars) = square(var.x)
``````

Iâ€™m trying to figure out a way that doesnâ€™t involve rewriting the same things.
Code generation might be the one I am looking for:

``````op = (:square, :cube)
for p in op
@eval \$p(var::Var) = Var(\$p(var.x))
end
``````

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