4 ms·
"As I’ve mentioned above, I like to use modules that expose groups of functions. These functions accept state and other dependencies. These modules tend to look
by BiteCode_dev 7y ago
"As I’ve mentioned above, I like to use modules that expose groups of functions. These functions accept state and other dependencies. These modules tend to look like what my colleague Drew describes as the functional module pattern."
I dislike that.
When I see a class, I expect it exists because there is a central state around which the methods revolve, and are meant to expose or change it.
When I see a function, I expect it to be side effect free.
Sure you can use any paradigm to make any program, but I use paradigms, just like I use design patterns, to communicate intent. Solving the problem is half the battle. In a way, coding style is documentation. I think we should build boring API, and only use cleverness to find solutions, or make the API easier to use and more elegant.
I worked once on a code base that broke a lot of implicit expectations. Like, it could add objects in a registry when you instantiated some classes, get_stuff could set other stuff, do_stuff would not do stuff but actually_do_stuff would, etc.
It was a nightmare to work with, because anything could happen anywhere. You had to look at the code every single time and keep the whole system in your head, which had 900k lines of highly technical code.
- rclayton 7y agoThis is a great reply. If you don’t use classes to define behavior for objects that manage their own state, there’s no point in using OOP.
- gchamonlive 7y agoI don't think you actually do state management in OOP, only manage a single level of abstractions. In java, inheiretance is directional in a single way, but if you instantiate a singleton component, you can end up affecting the state of every other class that uses that same component. This is not wrong per se, it is a property of OOP in itself. If this rubs you the wrong way, possibly you should be working in purely, monadic functions.
- BiteCode_dev 7y agoIn think singleton has been labelled as anti-pattern in OO for a long time. Having a single reference is useful, but using instantiation as a way to get it has proven a bad design decision.
- gchamonlive 7y agoThat is really my point. Isn't it better to just use a tool that limits what bad decisions you can make? Or at least make these bad decisions so hard that once you know how to implement them, you know better ways to write your code?
- bernawil 7y agoOOP has come to mean different things at different times. For a time, OOP definitely meant tightly coupling state and behavior. Same as inheritance, this practice seems to have been left behind some time ago.
- jvanderbot 7y agoYou misunderstand, I think. Or maybe I do. If done properly as described, the functions accept state. This means they adjust state by adjusting input/output parameters, not by affecting some internal state. This is basically Dependency Injection extended to state. This is also how classes are implemented (see python's self parameter to all class functions).
- BiteCode_dev 7y agoYes, but looking at the function, and banning some project specific naming convention, there is no way to know its role is to mutate a state. While in Python, when you see a class, you just know the methods are mostly about "self". It helps making sense of the code immensely.
- jvanderbot 7y agoCorrect, but the effectiveness of the design is separate from its correctness. Latter of which is what I commented on. For effectiveness, I think it reduces ancillary class members, and encourages (necessitates!) dependency injection. For example, changing "delta t" in a game object update is trivial: update the one delta-t you have, and pass it into everything that needs it. With classes, there may be a temptation to have a delta-t member variable, which costs many updates. Stupid example, but a mistake which is not possible with Dependency Injection. But really, proper scoping in typed languages (and naming in dynamic ones) can allude to input vs output parameters. In C/C++ this is passing by ref vs value. (In Google C++ style guide, they recommend use of pointers for "adjusted" or "output" parameters.) In MS C++ from back in the day, I think they had ways to explicitly specify parameters as input, output, or input+output. Does someone have an example of this?
- bhandziuk 7y agoplus favoring mutability over immutability opens up a whole other can of bugs
- hhas01 7y agohttps://en.wikipedia.org/wiki/Abstract_data_type https://en.wikipedia.org/wiki/Abstract_data_type
- klyrs 7y agoWhen I read "these functions accept state and other dependencies," I took it to mean that "state and other dependencies" would be an ever-growing list of arguments to the function -- not that they'd necessarily be modifying external state. I work on a project where I framed up both patterns, and ultimately decided that the hodgepodge^ pattern was preferable to repeating long lists of arguments. ^ kinda like the mixin pattern but I think it's been taken too far
- bernawil 7y agoThese functions accept state and other dependencies That "dependencies" term is very loaded. You need to define if you call "dependencies" what the functions in your module take as argument or you're talking about those other modules your module calls. When I see a function, I expect it to be side effect free. Why? though I think it's a commendable goal it's not a reasonable expectation from most languages.