Hi, folks.
I'm implementing an algorithm and must pass many arguments to the init function in a module that does the configuration. Then, those parameters will be used in many functions, including passing them to functions in other modules. I was wondering what the best way of doing that in Nim is.
For instance, the code below is how it's commonly done in Python. Since it's a class, the methods can access those attributes, meaning I won't have to pass them as arguments. In Nim, should I create all those "self" attributes as globals?
def __init__(
self,
loss,
*,
learning_rate,
max_iter,
max_leaf_nodes,
max_depth,
min_samples_leaf,
l2_regularization,
max_features,
max_bins,
categorical_features,
monotonic_cst,
interaction_cst,
warm_start,
early_stopping,
scoring,
validation_fraction,
n_iter_no_change,
tol,
verbose,
random_state,
):
self.loss = loss
self.learning_rate = learning_rate
self.max_iter = max_iter
self.max_leaf_nodes = max_leaf_nodes
self.max_depth = max_depth
self.min_samples_leaf = min_samples_leaf
self.l2_regularization = l2_regularization
self.max_features = max_features
self.max_bins = max_bins
self.monotonic_cst = monotonic_cst
self.interaction_cst = interaction_cst
self.categorical_features = categorical_features
self.warm_start = warm_start
self.early_stopping = early_stopping
self.scoring = scoring
self.validation_fraction = validation_fraction
self.n_iter_no_change = n_iter_no_change
self.tol = tol
self.verbose = verbose
self.random_state = random_state
Thanks.
Globals are not a great way to do this. Typically you'd make an object which holds all your fields, and then pass that object around. It's pretty much the same thing you'd do in Python, just that procedures don't belong to the object, they simply take this object as the first parameter. Because Nim has UFCS you can also call any function with its first argument before the call, similar to what you would do in an object-oriented language.
stdout.writeLine("Hello world") # Is the same as:
writeLine(stdout, "Hello world") # and also:
stdout.writeLine "Hello world"
This way you get the best of both worlds.In general, procedures with many arguments are awkward. Bob Martin's book, Clean Code, explains why. But there are always exceptions.
Reading through your arguments, it seems like they could be grouped into categories, and you could create a struct per category.
In my projects, I often have State, Settings and Config structs for each resource (thread, object, actor, etc.). State is private to the resource and usually initialized to some all-zero or value representing "unknown". Settings are private to the resource, have setter procedures and are initialized to default values often by reading from a non-volatile storage. Settings change throughout runtime. Config is initialized at the start and doesn't change without resetting the subsystem/thread/process. Sometimes Config is fixed at compile-time.
In general, procedures with many arguments are awkward.
Yes. In general.
Bob Martin's book, Clean Code, explains why. But there are always exceptions.
The book is terrible though, in general. ;-)