6 ms·
One interesting thing that is easy to notice about all of the examples of in the article is that they are absolutely infested with objects. I don't have anythi
by nordsieck 6y ago
One interesting thing that is easy to notice about all of the examples of in the article is that they are absolutely infested with objects.
I don't have anything against objects, per se, but I think they tend to make unit testing much more difficult to accomplish. The closer your code resembles pure functions, the easier it is to do dependency injection and unit testing.
- disgruntledphd2 6y agoYup, this is very true. As long as you return something deterministic, unit testing is easy. It's where you need to handle mutable state with objects that things get trickier. Unfortunately, these are exactly the places where you most need tests. I'm a big fan of constantly returning things rather than holding state in objects, for specifically this reason.
- HelloNurse 6y agoA basic issue of encapsulation: objects shouldn't rely on the mutable state of something else, but only on their own.
- zarathustreal 6y agoAgree. To add to this, many unit tests that you might have to do become obsolete with a strongly typed functional language. At that point you’re basically only integration testing the API boundaries / external interfaces.
- pjmlp 6y agoThere isn't pure functions the moment you touch any kind of IO. Plus the same problem arises with modules instead of objects, which traditionally are even harder to customize.
- nordsieck 6y ago> There isn't pure functions the moment you touch any kind of IO. You can get pretty far with good abstractions and dependency injection. Go's io::Reader and io::Writer interfaces are a great example of this. The resulting functions aren't pure in a technical sense, but they're pretty easy to unit test none the less. > Plus the same problem arises with modules instead of objects, which traditionally are even harder to customize. Maybe you could elaborate. I really don't understand what you mean here. From what I understand, modules just scope names, they don't maintain state. I don't see how they have the same problems as objects.
- pjmlp 6y ago> You can get pretty far with good abstractions and dependency injection Which goes back to the article's point of having to write code that is unit test friendly. Now architecture decisions have to integrate interfaces that wouldn't be needed otherwise. > Maybe you could elaborate. I really don't understand what you mean here. Modules keep state via global variables, module private functions and the surface control that they might expose via public API for the module. Additionally on languages that support them, they can be made available as binary only libraries.
- nordsieck 6y ago> Which goes back to the article's point of having to write code that is unit test friendly. > Now architecture decisions have to integrate interfaces that wouldn't be needed otherwise. You're not wrong. But in the context of functions, that doesn't seem to me to be particularly onerous. If the worst I'm forced to do is change the type of my parameters to an interface instead of a concrete type, that seems like a pretty small price to pay for easy testability. Certainly a much smaller price than the examples in the article.
- pjmlp 6y agoYou are assuming that interfaces exist as language concept. Imagine doing unit tests for a C application, where modules == translation unit/static/dynamic library, thus you can only do black box testing. Now one needs to clutter it with function pointers everywhere, or start faking interfaces with structs, just for the benefit of unit tests. And with static/dynamic libraries than one might need to start injecting symbols into the linker to redirect calls into mocking functions. All just to keep QA dashboards green.
- monocasa 6y agoThat's how a lot of great C code is written anyway. A C library should abstract out logging, allocation, and IO so that the client code can change them out if need be. The fact that it makes unit testing easier is just icing on the cake.
- alexandercrohde 6y agoYes there is. What you do is have don't put any IO calls inside your pure functions, but rather pass in their results as parameters. Keep the impure code and the pure code separated.
- pjmlp 6y agoBeautifully said, practically impossible unless the language imposes it as programming model and you have 100% control over the complete source code.
- Nursie 6y agoMock frameworks make it trivial. Foo foo = new Foo(mock(Bar.class)) foo.a(x,y,z) Not sure what the issue is there really.
- nordsieck 6y ago> Mock frameworks make it trivial. In my experience mock objects can be brittle. A few sprinkled in judiciously can be ok, but once the density gets high enough, it starts to feel like the test becomes decoupled from the actual code it's supposed to test.
- loup-vaillant 6y ago> The closer your code resembles pure functions, the easier it is to do dependency injection and unit testing. If the only thing you inject is data, can we still call that "dependency injection"?
- nordsieck 6y ago> If the only thing you inject is data, can we still call that "dependency injection"? I suppose that's a philosophical question. Probably the 2 most common functions I 'dependency inject' are rand() and time.now(). I feel like they count, but you might not.
- g_delgado14 6y agoDefine data.
- loup-vaillant 6y agoAnything that doesn't have an arrow in its type.
- commandlinefan 6y agoYou're one sentence away from discovering Common Lisp ; )
- deleted 6y ago[deleted]
- Tainnor 6y agoI mean, if you pass a HOF to some other function, then that is also a dependency.
- loup-vaillant 6y agoCorrect, but I haven't seen it happen often in practice. I mean, pretty much every project uses HOF, but few have many of them. I also tend to avoid HOF when I can instead pass data around explicitly.