Naming variables and constructor functions

This is a style question that has been bugging me for a while so I thought I would ask what others do.

Consider a composite type Foo, for which I expose the constructor. This is something that I like to do for non-trivial types, separating the interface from the implementation (the type may have internal fields, and various ways to construct it, I find that this serves me better in the long run).

I like to name variables (fields, function arguments, etc) as foo. But then I no longer find it good style to also name the constructor function foo (even when possible). Julia is a lisp-1, so variables and functions share the same namespace.

My solution is naming the constructor make_foo or calc_foo, depending on the complexity. make_foo is when the constructor just packages up values and does some trivial checking. calc_foo is when the CPU gets a workout.

Similarly, this means that whenever I need an accessor function that gives me foos, I name it get_foo, not foo. An example of bad style is something like

time = time() # within some local scope

which is permitted, but after that say goodbye to time(). I find it irksome when something similar happens in my code.

I am curious what others do.

I assume this is something you have considered already, but what opposes just overloading the constructor instead of make_foo?

1 Like

I lean toward this style:

b = bar()
q = qux()

When function bodies are small, the short variable names are understandable and pretty readable. But for longer functions with more complicated logic, you do tend to need longer variable names…

1 Like

Is there a reason you prefer to name your constructor foo rather than Foo? So long as you leave the default constructor alone you can maintain the separation between internal representation and the interface to construct your Foo, or enforce an internal constructor if you prefer.

1 Like

For larger, more complex projects, I like to consider types (both abstract and concrete) as internal implementation details, not part of the exposed API. Once Foo is part of the API, all of its methods are.

Also, if I am refactoring the code, finding a make_foo is easier than looking at all instances of Foo, which might just be types for dispatch etc.

I find default constructors (with Base.@kwdef when they have too many fields) much easier. Eg if you want the constructor to apply conversions and promotions, you have to write methods for basically boilerplate code.

2 Likes