I am writing some code with structs that represent dual forms of the same underlying thing, e.g.
struct NormalForm
normal_data
descriptive_parameter
end
NormalForm(data) = NormalForm(data, process(data))
struct DualForm
dual_data
descriptive_parameter
end
DualForm(data) = DualForm(data, process(data))
I also have functions which convert between them:
normal_form(df::DualForm) = NormalForm(lambda(df))
dual_form(nf:NormalForm) = DualForm(lambda(nf))
My instinct was to write the conversion functions as distinct functions like this, rather than as new methods for the type constructors. But since they do the same thing that a constructor method would, it has occurred to me that they could just as easily be constructor methods. But should they?
The built-in struct String
is similar – there is a constructor String()
, and there is the conversion function string()
. Both return objects of type String
. So why is string()
defined separately?
On the other hand, there is not an int()
function. If I want to convert another numeric type to an integer, I have to call the type’s constructor, Int()
.
The docs suggest that conversion functions should not create new objects if they don’t have to, while constructors always should. That might explain the difference between String
and Int
, and may suggest that the constructor approach is correct here, since I’ll never call dual_form
on a DualForm
object?
Defining e.g. Base.convert(::NormalForm, x::DualForm)
would work, but it seems like that’s really only designed for implicit conversions rather than explicit ones like I want here.
So what is the preferred style for writing a function which returns objects of user-defined types? Is a constructor method more idiomatic (as in Int()
) or are distinct conversion functions (as in string()
)?