Type() vs type() -- Are constructor methods or conversion functions more idiomatic?

SortofDamocles
Thu, 19 Jan 2023 20:41:20 +0000
General Usage

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())?

5 posts - 5 participants