last day (15 days later) » 

13:56
89
A: Multiple boolean arguments - why is it bad?

FlaterThe obvious problem To resolve this however, how would one refactor this? Do you create 4 functions now? trainModelWithZerosInitOptimSGD(...) trainModelWithZerosInitOptimAdam(...) trainModelWithRandomInitOptimSGD(...) trainModelWithRandomInitOptimAdam(...) What you've done here is the ...

What about passing a single extra parameter in the format Object / Associative array? It seems to me a good solution for a language that doesn't have named parameters. You then have readability for your options in the call, while being able to fix an existing function by adding a functionnality without refactoring existing code
Good answer, The only difference i would have done is to have a different naming for the builder solution, as I would expect a method named "train()" to actually do the training, and not just make an object that i can use to do that.I would call that function "getTrainer()" and then i can invoke "train()" on it to run.
@Kaddath A patchwork upgrade is not better than a good design. I am not in favor of just adding an extra parameter. If the code warrants a change, it warrants re-evaluation of what the correct design is. Only when you did things uncleanly in the past will this change be painful. And when you did things uncleanly, that warrants needing to clean it up. If you stick to clean coding guidelines, changes are generally not painful enough that you'd want to find a patchworky solution instead.
@Flater Your point is valid in theory of course, maybe we should live in a perfect workplace where all coders are highly ethical, where the code base is perfect and you have unlimited money and time to develop features. Unfortunately I don't, and I would have hard time to convince my boss that it will take a week to add a simple extra feature because I need to go through tens of thousands of lines of code to refactor and re-test everything. I do it when I can..
re: builder pattern - At least in js I find it very difficult to write unit tests against. For instance in order to verify 'run()' is called, you need a mock which exposes the underlying builder and to make sure each method returns the instance of itself. You then need access to the instance such that if multiple instances exist that each can be asserted separately from each other. I've recently ran into this problem in knex - and prefer apis that allow you to configure them e.g. const myTrainedModel = buildMyModel({ train: true, withWeights: 'random', withOptimizer: 'SGD', run: true }),
13:56
In Python (which the question author appears to be using), the builder pattern is mostly obviated in practice by the presence of keyword arguments, so you can specify train_model(my_model, use_sgd=True, init_with_zeros=False). Furthermore, you can enforce the use of named/keyword arguments for certain parameters by designating them as "keyword-only" parameters. For this reason, the builder pattern will generally come across as un-Pythonic and awkward, because it's largely unnecessary.
An alternative for languages without named arguments (like Lua and JavaScript) is to pass in a table: train_model(my_model, {use_sgd = true, init_with_zeros = false})
Love the answer yet I stopped after the killer intro. Suddenly, underneath the OP question, I gleaned a fundamental not understanding of objects. This invisible hand treats OOD/P like a JC Whitney parts catalog. At the end of which in bold letters is written SOME ASSEMBLY REQUIRED.
@Kaddath: If you can implement a feature that touches "tens of thousands of lines of code" in a week, that's great productivity. I think the underlying problem here is your boss' expectation of how long a feature implementation should take, which is causing you to lose out on necessary quality work which then leads to everything else taking more time because the codebase has turned to shit. Your boss cannot fight the fight for quality since they don't understand it. You do, and the onus is on you to maintain the quality. Learn to communicate to your boss that the quality is necessary.
There is another problem here not mentioned (AFAICT) - what if there is a combination of booleans that doesn't make sense, or has some "don't care" outcome? Eg. if the first bool being false makes the second one redundant, you either have weird variance (sometimes changing the second parameter affects the outcome, sometimes not), an avoidable error (true, true returns an error), or just extra details to document (author)/not overlook (user). In this case, enumerating the valid inputs with a dedicated type is often more correct.
As a real life example of the RaiseToPower concept, in the Redshift SQL dialect (maybe others as well), datediff(unit, b, a) does not map conceptually to b - a, violating centuries of arithmatic convention, but maybe satisfying someone who wishes subtraction followed the ordering of the number line?
@Flater : But we can and should again ask the "why" (perhaps 4 more times!). Why would, say, simply going and passing a single extra parameter with labels be still an unadvisable design, in the same spirit as to asking why the original one was? Moreover, as you point out, the guideline isn't to be taken absolutely tightly, so how or what can you absolutely call "sticking to the clean coding guidelines" vs. "not"?
If the main concern is the readability, then a struct with labels makes the parameters legible. Since the concern is addressed, why are we continuing to question the design? If the design now must continue to be questioned, we must then have something else, beyond just the readability, that is motivating that ongoing questioning - and/or the readability is still not as good as it could be.
That said, I would agree the inversion of control method helps something else - it makes the method more extensible without modifying it, i.e. the "open/closed principle" for the method - which you may or may not need, but given the fact there are almost surely many more such filters out there, one likely will need to extend this. Now, if this is the answer to the second "why", then it should be made more overt/explicit in the answer post, I think. I.e. say, "this [labeling/etc.] solves the readability problem - BUT there is a second design problem here, which is extensibility ...".
@Flater: (to clarify on my original question, what I mean by "guideline isn't to be taken absolutely tightly" vs. "stick to the guideline" - the point I'm trying to make there is that it is unreasonable to expect code will ever be "perfectly designed" out of the box, esp. when guidelines tart to contradict each other; there, you have to make a choice, and then see by retrospect and consequence [trial and error] what the "correct" design is. So rewriting/refactoring code is normal and essential part of programming. The trick is how you can avoid getting into "rewrite/refactor hell".)
(Which produces no code [that gets used], and no code is worse than bad code [often]. The "proper" attitude, then, to guidelines, in my experience both with traps of extensive rewrite hells and literalism, should be - follow them until you see a contradiction. When contradiction appears, sacrifice one for a "more important" others to restore logical consistency and thus possibility to implement. Learn by experience whether the resulting design is good. Refactor/improve if it is not. In any case, make sure you are moving forward with the code. Iterate, rise, repeat, and iterate some more.)
13:56
@The_Sympathizer: Comment 1: Because it's a step on this road. Comment 2: I did not talk solely about readability. Comment 3: A discrete section of clean code cannot be attributed to a singular principle, overlap between good guidelines is inevitable. Comment 4: I make no claims of perfection, only betterment. Comment 5: Guidelines exist in absence of experience. Eventually you can do it by experience but initially the guideline is there to help you along the way. Applicability of a guideline is highly contextual.
@Flater: Sure. Though I'll say for comment 5, in my experience of having been "absent of" experience, I found the extreme stricture on guidelines to lead me very often to seemingly-insuperable contradictions resulting in never producing a finished product at all.
@The_Sympathizer There's a reason I bolded the core part of my answer, as it addresses precisely that point you're making (which I think OP is also stuck on right now).
@MatthewDrury: "satisfying someone who wishes subtraction followed the ordering of the number line?" Start dates are always put before end dates (of the same range), even though the timespan inbetween would still be calculated as b - a. You're coming at this from a mathematical angle, but I can make a reasonable claim that the readability of the method signature should not depend on knowledge of how the method will be implemented. The method signature is date-based so it should follow date-ordering conventions. It's a great example of such a conflict though, +1 for that.
@Flater : Yes. I wish I'd understood that much longer ago, though.

last day (15 days later) »