6 ms·
Interesting, I would have said static typing allows more technical debt. Illustrating example: Lets say you pass a double variable from some part of your code t
by david_draco 7y ago
Interesting, I would have said static typing allows more technical debt. Illustrating example: Lets say you pass a double variable from some part of your code through 13 layers of APIs until it is actually looked at and acted upon. Now you realise that you not only need a double, but also a boolean. In dynamic typing, you can make a tuple containing both and only modifying the beginning and end points. In static typing you have to alter/refactor the type everywhere.
- toastal 7y agoAnd on layer 10 you missed that you were using and treating the argument as if it were the double. Now you have a bug that the type system would have solved. Or you can alias that input early if you know with a good certainty it has possiblity to change, and now you just update the alias and everything gets checked all the way down without a refractor.
- k__ 7y agoAFAIK, the problem here isn't "bad static typing" but "bad type inference" If you only have to write the type once at the top of the 10 layer, you don't have to do much to change it.
- codeflo 7y agoIt’s often considered bad practice to pass around raw values for that very reason. Introduce a (minimal) abstraction, that is, give the thing you’re passing through those layers a name. Then you can change the endpoints at will and still get static checking (as a plus, you can‘t accidentally pass the wrong bool, or mix up the tuple order). I agree with the GP‘s point that static typing forces you to do that kind of design work earlier. (Edit: You raise a good point, though. I think a lot of people run into this kind of problem with static typing.)
- caseymarquis 7y agoI think at that point you probably need a major refactor, regardless of the language being used. While I don't want to be in that situation, I'll typically use a helper class if I'm forced to pass something through 13 layers (a class containing all the arguments). However, that's pretty much the textbook example for designing with dependency injection in mind. Static typing won't fix a terrible architecture.
- galangalalgol 7y agoI haven't read that textbook. Do you have a good reference. I may have been intentionally been using an anti pattern. I often have a POD class that gets passed down a processing chain.
- caseymarquis 7y agoYMMV. There's probably at least one good use case for virtually every design. What you're doing could be totally reasonable. The original description sounded like 13 functions being manually chained together, each inside the last. Where to go from there is very situational. Using a POC is pretty much a given. For transformation in the simplest form, I'd typically have a series of extension methods (or some equivalent, like a transformation class which only has methods and holds a reference to the object) which can be called to transform and return the object or return a modified copy of the object. That's easier to test and debug as each method doesn't need to know about the one above/below it (they get called in parallel). You can also easily modify the order of the transformation, or cut out parts of it. When testing/debugging, you just initialize data to some state, then test the broken piece of the pipeline. If the 13 vertical calls are for initialization, DI allows you to turn that into 0 calls. You probably need reflection in your language to make DI work though. I've never liked DI frameworks built by others, and have generally made my own. All that said, I'd stand by my point that if 1 item is passed vertically through 13 functions before it gets used, then there's probably some way to either make things a bit more horizontal, or to automate the passing. Another strong belief: If DI or some other "good" design tool results in more lines of code or significantly more confusing code, then either it's being done wrong, or it isn't applicable to the situation. There are generally more wrong places to use a tool or pattern than there are right places to use them. A power drill is a terrible hammer.
- galangalalgol 7y agoI'm keeping that line. More wrong places... That is koan worthy. What is DI dynamic initialization? I can't imagine passing a const bit of POD through 13 layers without it bein used, but not every layer uses all of it probably.
- sethammons 7y agoYou'd love Perl. Just pass @_ through your callstack. Want a new value available 20 functions deep that is available at the top? Just toss it in @_ at the top and you are done. The problem? Every one of your 20 levels of functions/subroutines has an unnamed grab bag of variables. You get to keep that in your head. If you want to know if you have a value in a given branch of code, your best bet, aside from reading the code of the entire callstack, is to dump @_ in a print statement and run the entire program and get it to call the function you are in. Oh, and if one of those values contains a few screens worth of data, you will need to filter that out manually. Even "documentation" in the form of comments or tests will be unreliable due to comment-rot or mocked test assumptions. Even in Python, I'll often have to go up the callstack to know what a named parameter actually is. And if similar shenanigans are going on, I again have to pull out a debugger or print statements to know what I can do with an argument. With a static type, I see plain as the text on my screen what type I have as a parameter and I immediately know what I can do with it. As weak as Go's type system is, it is worlds better than Perl and Python for maintaining and creating large, non-trivial codebases. The price is passing it around at the time of writing.
- dmux 7y agoIsn't passing @_ around similar to a concatenative language pushing and popping things off the stack?
- sweeneyrod 7y agoYou could also only modify the endpoints in a statically typed language with type inference.
- _pmf_ 7y ago> Lets say you pass a double variable from some part of your code through 13 layers of APIs until it is actually looked at and acted upon. Now you realise that you not only need a double, but also a boolean. In dynamic typing, you can make a tuple containing both and only modifying the beginning and end points. In static typing you have to alter/refactor the type everywhere. Adding a double and boolean field to a Context object, I don't have to touch anything at the intermediary API layers. Just as with your tuple/dict.