Live data from Hacker News

The use of `class` for things that should be simple free functions

quuxplusone.github.io

251–260 of 406 posts

Re: The use of `class` for things that should be simple free functions

#251
post #229

Earlier quoted context omitted.

Seems like kind of a meaningless gesture to pass in your own “this” just because you don’t like OOP. If you need a class, why not write it the idiomatic way?

In what way do you 'need' a class? It uses none of the features that a class provides over normal structs.

The idea is, if you need the functionality that classes give you (passing the implicit state between multiple calls), why not use the special syntax for that use case?

Re: The use of `class` for things that should be simple free functions

#252
post #229

Earlier quoted context omitted.

Seems like kind of a meaningless gesture to pass in your own “this” just because you don’t like OOP. If you need a class, why not write it the idiomatic way?

I didn't down-vote you, but in my opinion it's not meaningless because it makes it inherently more testable. Try testing an opaque class without any accessibility into its state. It is much more difficult. If it takes in the state, performs an operation, and returns a new version of that state (ideally an immutable copy) then it becomes much easier to test and validate. The reason information hiding is/was advocated…

    Tests.h
    #define private public
    #define protected public

Re: The use of `class` for things that should be simple free functions

#253

Earlier quoted context omitted.

I didn't down-vote you, but in my opinion it's not meaningless because it makes it inherently more testable. Try testing an opaque class without any accessibility into its state. It is much more difficult. If it takes in the state, performs an operation, and returns a new version of that state (ideally an immutable copy) then it becomes much easier to test and validate. The reason information hiding is/was advocated…

Isn't how you pass things into a function separate from whether you are hiding information? If your state parameter was an opaque pointer then you wouldn't necessarily be able to do anything with it. Also if your class internals were public you would be able to change anything you wanted.

Exactly! You don't need classes to obtain information hiding. In general treating the state as an opaque pointer is a good thing. But the state is part of the public API whether you want it to be or not. Even if you try to "hide" it in the OOP sense, the result of the methods depends on the internal state so it is leaked through the behavior of those methods.

However, when it is hidden in the OOP sense it makes it much harder to reason about the behavior of a function without reading the source code. Because there is this internal "hidden" state that you do not know exists.

But a referentially transparent function is much easier to understand. For a given input, you get an exact output.

Re: The use of `class` for things that should be simple free functions

#254
post #244

I had to look up what memoization is- guess I’m getting rusty? But it turns out it’s just a new word for an old concept (caching). I’ve always done it this way: double calculation(double arg) { thread_local bool prev_arg_valid = false; thread_local double prev_arg; thread_local double prev_result; double result; if(prev_arg_valid && prev_arg == arg) result = prev_result; else { //do the calculation result = ...; prev…

> I had to look up what memoization is- guess I’m getting rusty? But it turns out it’s just a new word for an old concept (caching). The term memoization was coined in 1968 and quite possibly predates the term cache (with respect to computing).

Interesting!

I still maintain though that memoization is a special case of caching with n=1. :)

Re: The use of `class` for things that should be simple free functions

#255

Earlier quoted context omitted.

None of these situations require an object to do. You can always have a function that takes in an extra ‘state’ argument. You can then do everything you said by passing in the corresponding state values.

Sure, you can build your own object and pass the 'this' pointer around manually. You could use C for everything, and tediously do everything again that objects do for free. But why? There's a lot of object-hating going around. Its silly. Use objects to encapsulate functionality, they are good at that and everybody understands what it means.

There isn't object hating it's just writing the simplest program that does what you need, having n layers of indirection requires the programmer to keep context of n layers. If you need the indirection create it when you need it.

Re: The use of `class` for things that should be simple free functions

#256

Counter-point: while in many situations this isn't the right approach, it's worth recognizing when this is the right approach, because they can look awfully similar. An example of this is graph searching, e.g. BFS or Dijkstra's algorithm. The typical implementation is a function. But if you make Dijkstra a class, with (say) a function to iterate through nodes, it lets you do several things that would be difficult wit…

You still hit the same issue and you can still solve it the same way by converting that class with saved state into just a function.

Below are two short examples of two different ways to do this:

   type Node = Null | {value: int, left: Node, right: Node} 
   //a Node is NULL or a value that is an int with left and right nodes

   //procedural with mutation, saved state is placed in parameter: found
   def find_n_and_print_all_DFS (node: Node, search_value: int, &found: Node) -> Void:
      if node == Null:
         return
      else:
         print(node.value)
         *found = node.value == search_value ? node : Null
         find_n_and_print_all_DFS(node.left)
         find_n_and_print_all_DFS(node.right)


   //functional, no mutation, saved state is returned
   def find_n_and_print_all_DFS (node: Node, search_value: int, found: Node = Null) -> Node:
      if node == Null:
         return found
      else:
         print(node.value)
         found = node.value == search_value ? node : Null
         left = find_n_and_print_all_DFS(node.left, found)
         right = find_n_and_print_all_DFS(node.right, found)
         return left != Null ? left : right


I would argue that if I used classes the code would be way harder to read and much more verbose.

You can essentially use tail recursion on that DFS and pass in some accumulator as a parameter into your DFS or whatever. That accumulator can be some pointer representing some saved state or anything you want. There is still no need to group everything together into an Object.

It's a bit trickier but in the second example you can see that you don't even need to mutate anything at all. What you described can be achieved without redundant OOP classes and without mutating state at all! Additionally setting found to have a default value of Null negates the need for the extra accumulator parameter during function calls as you can just call it like this:

     saved_node = find_n_and_print_all_DFS(x, 5)
I would still say OOP is an anti pattern because it overall promotes adding these mutating parameters in your code even when you don't need save states or anything of that nature. In OOP, the existence of getters and setters and methods for accessing variables outside of the definition of the method itself heavily promotes this type of coding style regardless of whether or not it is needed.

The procedural style forces this additional "saved" feature to be evident as an extra parameter. If you never use that parameter it becomes obvious that the parameter is redundant while in OOP mutating external state is an intrinsic part of the style and like the OPs example you have to go through several logical leaps to see how redundant it is.

Re: The use of `class` for things that should be simple free functions

#257
post #118
post #91

So one of the problems with that function is that it's global. The advantage of having a single class that either has a hidden constructor and only a static function or needs to be initialized but only offers the stateless function is that you know what concept the function is related to. Sometimes I have an entirely stateless class that has a cluster of 10 functions, both private and public just to have that particu…

If you use c++ (as the example in the article) you can just use explicit namespaces at least.

True, I'm working with Swift which doesn't have that concept. But you can't have collisions between similarly named functions that are in the main or a different namespace? For example when somebody defines it's own version of 'print'?

Re: The use of `class` for things that should be simple free functions

#258

Earlier quoted context omitted.

None of these situations require an object to do. You can always have a function that takes in an extra ‘state’ argument. You can then do everything you said by passing in the corresponding state values.

Sure, you can build your own object and pass the 'this' pointer around manually. You could use C for everything, and tediously do everything again that objects do for free. But why? There's a lot of object-hating going around. Its silly. Use objects to encapsulate functionality, they are good at that and everybody understands what it means.

But you _are_ passing `this` around with OO code too. It just ends up being before the dot (or arrow), instead of being an argument. All you did is loose some flexibility.

OO couples behavior with state. Sometimes that's exactly what you want (e.g. containers). And sometimes you just need data, functions and namespaces and all 3 are available in C++ outside classes.

Re: The use of `class` for things that should be simple free functions

#259

Although he's correct at a high level, I'd say this should be handled more like the advice given to programmers on optimization: Optimization for beginners: Don't optimize Optimization for experts: Don't optimize - yet. What I've observed, over and over (for the past 20 years of Java development) is that beginning programmers don't really see much point in object-oriented design, and default to static functions (Java…

Composable maybe, but testable, not necessarily.

If the function doesn't read or modify a global state, it can be much easier to test in isolation than a class, which might e.g. throw an exception in the constructor causing your test to fail before you even get to run your method.

If it does touch a global state, it's probably instead better as part of a class.

Re: The use of `class` for things that should be simple free functions

#260

Earlier quoted context omitted.

You don’t agree with an idea so it’s silly? Passing around state via variables is no more tedious than the extra syntax for class definitions, constructors, member variable access, extra semantics related to objects, extra keywords related to visibility, etc. You can encapsulate functionality without objects. There is nothing that you can do with an object that you can’t do with a closure, and the closure version wil…

Though i agree with with your general statement, i think the 1/10th the size figure is a bit disingenuous. At least the cases where i've gone from the unnecessary OO style to a simpler function/procedure based solution (in JS, which supports both styles just nice), the code size difference hasn't been anything that great. At most it's been half the size. Which is a lot i think! But not an order of magnitude less. I'm…

For JS, it's best to avoid class as possible, due to this and bind / apply shenanigans.

Closure and function return function can be used as substitute, and except for inheritance (which should be avoided too), I haven't found any use case for using class class. Example:

    function MyList {
      let data = [];
      let add = (item) => data.push(item);
      return { add };
    }
Post reply on HN