4 ms·
If you can't read my example I am sorry to tell you, you don't have the necessary skill set to work as a programmer. What I wrote is much simpler then ANYTHING
by deltaonefour 4y ago
If you can't read my example I am sorry to tell you, you don't have the necessary skill set to work as a programmer.
What I wrote is much simpler then ANYTHING in the article. If you can read the article you can read my code. If you can't then I'm sorry.
- kgeist 4y agoIn your example, logFilteredWithDateToFile accepts a file. That means, if a function wants to log an error, it needs to accept a file somewhere to pass to the log function (for example, we want the sockets/files to be opened once and be shared across several calls). So if function A depends on function B which depends on function C, then function A will need to explicitly state all the dependencies of B and C in its function signature. We end up with a very complex function signature of A where we have to list all possible dependencies of every function it makes use of recursively (also, some of the dependencies may not always be needed depending on control flow). As a client, all I wanted was to ask a function to do one simple thing but now I have to manage files, sockets and god knows what else just because somewhere deep inside someone uses them. And then in a new version we decide we don't want to log to a file, we want to use a third-party library. Now we need to change signatures of A, B and C, and modify our code at every call site. Is this what you are proposing?
- deltaonefour 4y agoPeople interpreted my initial reply as arrogance. While it is a bit arrogant, there's a reason for the condescension. Because your flippant reply, implied you didn't even read the code. Any programmer can OBVIOUSLY read the code. You didn't and claimed you didn't UNDERSTAND it. Well here now, you have a full retort and explanation... so were you lying when you said you didn't understand it? Obviously you were. So my equally flippant response was appropriate. But let's get that out of the way. I appreciate your second reply here. >In your example, logFilteredWithDateToFile accepts a file. That means, if a function wants to log an error, it needs to accept a file somewhere to pass to the log function (for example, we want the sockets/files to be opened once and be shared across several calls). This is the intention of how the library works. It actually does promote the sharing of files and sockets. See trivial example below: s = WrappedSocket(...) logStringToSocket(s, "hello") logStringToSocket(s, "world") See. Socket is shared across several calls. The only thing that's a bit of the eyesore is that extra socket being passed around. But this is a problem that can't be gotten rid of. You either pass the socket all over your code or in the OOP example you pass an instance of a logging class around. The only benefit of OOP is that the logging class manages the lifetime of the socket. If calls socket.close() for you during destruction. That's it. >So if function A depends on function B which depends on function C, then function A will need to explicitly state all the dependencies of B and C in its function signature. No you don't. This is not mandatory. I expose only parameters that are needed by the user and the API. If I want to give the user the ability to filter I SHOULD expose two string parameters, s: str, match: str. because without it the user can't do anything. But I don't have to, see below: logHelloWorldWithDate() = print(appendSuffix("hello world: ", time.date().to_string())) appendSuffix has two parameters required. But I expose zero parameters. >We end up with a very complex function signature of A where we have to list all possible dependencies of every function it makes use of recursively There's no recursion going on in my example and none of the logging functions manage any state. Again the function signature is as complex as you want it to be. If there are OTHER required parameters like "socket" and you want to reduce the complexity of the parameters coming in, you can wrap say a socket and a file in a single struct. Not a huge deal either way... Often this variable is called ctx or context, you see it in languages that tend to be more against OOP like golang. For C++ you can have state managers. Often this exists in the form of things like ThreadPool or **Manager objects. These manager objects are more of a necessary evil though because they lack the huge compositional ability of my example due to the fact that they unionize state and function into a single primitive. Managers overall segregate state away from the rest of your program such that you can have huge compositional ability in the rest of your program as shown in the example above. >As a client, all I wanted was to ask a function to do one simple thing but now I have to manage files, sockets and god knows what else just because somewhere deep inside someone uses them. Yes as a client you do need to manage this. I mentioned this problem with the lifetime of socket or file. From the perspective of say a programmer who uses garbage collection it's the same amount of crap being passed around. s1= socket() s2=socket() logWithSocket(s1, "hello") logWithSocket(s2, "world") versus the oop version l1 = LoggerThatOwnsASocket() l2 = LoggerThatOwnsASocket() l1.log("hello") l2.log("world") Either you're passing a socket everywhere or your passing the log manager everywhere. If you want something that logs to both socket and a file (multiple dependencies) simple package both of those dependencies in a single struct. This struct can be used for both the OOP version and the non-OOP version. So from the perspective of a programmer who uses garbage collection, the problem presented by both examples here is the SAME, but the 1st example is superior because it is MORE composable and modular. >As a client, all I wanted was to ask a function to do one simple thing but now I have to manage files, sockets and god knows what else just because somewhere deep inside someone uses them. Either you're managing a bunch of LogManagers with all the dependencies, Or you're managing a struct with all the dependencies. The problem is the SAME. >And then in a new version we decide we don't want to log to a file, we want to use a third-party library. Now we need to change signatures of A, B and C, and modify our code at every call site. Explain this problem with an example. It's too vague. What is the nature of the third party library, what is it replacing? >Is this what you are proposing? I've hit all your points pretty thoroughly sentence by sentence. The only thing I haven't resolved is the third-party library thing. Feel free to explain that part and also offer counter arguments to my arguments.
- kgeist 4y ago>Socket is shared across several calls. The only thing that's a bit of the eyesore is that extra socket being passed around. But this is a problem that can't be gotten rid of. You either pass the socket all over your code or in the OOP example you pass an instance of a logging class around. The concept of a "logger" (defined as an interface, for example) isn't tied to any particular implementation. Imagine you have 1000 files that pass sockets around (a plausible scenario because logging is essential). The requirements change and we want to write to a file or use a third-party library. You will have to change 1000 files. If I use an interface, I only touch the initialization code (often 1 single file) by plugging in a different implementation - all 1000 files immediately start using the new implementation with no effort. That initialization could be a dependency framework of your choice, or manual initialization. In OOP, you usually don't pass dependencies around as function arguments, you inject them in constructors once somewhere at startup time and forget about it. So no, it's not only about calling the destructor. If you emulate this behavior with structs, it's still OOP, just without the syntax sugar.
- deltaonezero 4y ago>The concept of a "logger" (defined as an interface, for example) isn't tied to any particular implementation. Imagine you have 1000 files that pass sockets around (a plausible scenario because logging is essential). Interfaces are a separate topic. In my example, I log to TWO sockets. If you want to imitate the same thing with a log manager you need TWO instances of LogManager. If you have 1000 files you either pass the socket around to ALL 1000 files or you pass the LogManager around. Same thing. The logManager is simply a wrapper around the Socket. >If I use an interface, I only touch the initialization code (often 1 single file) by plugging in a different implementation - all 1000 files immediately start using the new implementation with no effort. Interfaces are a different topic for a different use case. But your point also doesn't fly with me. Because you can REDEFINE a function as well. You define the function in one file and it's used in 1000 files. You redefine it in one place it's redefined in everywhere. >That initialization could be a dependency framework of your choice, or manual initialization. So? I can initialize dependencies and pass it around as well. Same thing as passing a log manager everwhere. d1 = DependencyManagerSocketFilesEtc() d2 = DependencyManagerSocketFilesEtc() logStuff(d1, "blah") logStuff(d2, "blah") versus: l1 = LoggerWidthDependenciesFilesSocketsEtc() l2 = LoggerWidthDependenciesFilesSocketsEtc() l1.log("blah") l2.log("blah") l1, l2, d1, d2, need to be passed Everywhere your program is used. There is no getting away from this. Doesn't matter if you use OOP or not. But you'll note one thing. d1 and d2 are reusable. and modular. I can reuse sockets and files with other things. With a logManager I don't even have access. Which is my point. deleteContents(d1) >If you emulate this behavior with structs, it's still OOP, just without the syntax sugar. I am not emulating this behavior with structs. Just because they tell you in C++ 101 that a struct and a class is the same thing doesn't mean it actually is in a general sense. A struct has no methods in general. It's just raw data. A class is more of a mixture of data AND methods. OOP is the later not the former. >In OOP, you usually don't pass dependencies around as function arguments, you inject them in constructors once somewhere at startup time and forget about it. You aren't hearing my argument. Injecting the dependency into a class necessitates the ENTIRE class to be passed around. You either pass the dependencies or you pass around the wrapper class they are injected into. Let me give you an example with a higher level of scope. s1 = socket() s2 = socket() doStuff(s: socket) { ... logToSocket(s, "blah") ... ... } doStuff(s1) doStuff(s2) versus: l1 = logManager() l2 = logManager() doStuff(logWithSocket: logManager) { ... logWithSocket.log("blah") ... } doStuff(l1) doStuff(l2) Virtually no difference. Think of LogManager as SocketWrapper. Just putting a socket in a wrapper doesn't change the fact that you have to pass it around all over the place.
- motogpjimbo 4y agoYou should probably tone down your arrogance given that the very first example in your list has an error in it.
- deltaonefour 4y agoIt's written in psuedo code. And what error.
- motogpjimbo 4y agoYou've used the same symbol twice in the parameter list. I'm surprised a genius like you can't spot it.
- deltaonefour 4y agoWhere? I need you to spell out the line for me where I used the same symbol twice, because I don't see it. The post is well past the time limit for editing; so whatever it is you're talking about it's there forever. I also likely have dozens of errors in my psuedocode. If my arrogance pisses you off you should just take me down by proving my point completely and utterly wrong. The problem is, you can't. So you resort to being rude and pointing out trivial errors in the code. I'm pretty sure you don't have any argument here.
- motogpjimbo 4y agoI've already told you what the error is and where it occurs. If that isn't enough information for you then I am sorry to tell you, you don't have the necessary skill set to work as a programmer.
- deltaonezero 4y agoDo you realize why I said that to the other person? There's a good reason why I was rude. Why don't you read the thread. There's no good reason for you to be rude here. Unless you're an ass hole. Not saying you are an ass hole. But that's certainly the only reason why you'd act the way you are.