Live data from Hacker News

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

quuxplusone.github.io

391–400 of 406 posts

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

#391

Earlier quoted context omitted.

If you have a function `foo(a, b, c)` and you find yourself passing the same a and b all the time, you can curry it into a function that only takes c: `f = curry(foo, a, b)` such that you can just call `f(c)`. The OO alternative would be to create a class: class Foo: def __init__(self, a, b): self.a = a self.b = b def f(c): return foo(self.a, self.b, c) And then you create an instance `obj = Foo(a, b)` and when you n…

Ah I see, I believe you've mixed up currying with partial function application, that's what confused me (but, again, I'm not an expert on functional languages so it could be me that's mixed up). I believe that currying doesn't take any arguments except the function itself: # Signature of f is (a, b, c) -> r g = curry(f) # Signature of g is a -> (b -> (c -> r)) # i.e. g is a -> blah, # where blah itself is a function…

Yeah, you’re probably right. I don’t understand all the tedious, annoying functional jargon; I just know there is a function in Racket called “curry” which does what I described above. Sorry for my error.

> I suppose the real major thing is you've put a bunch of parameters into a single class/tuple/struct rather than spelling them out individually

Yes, this is the mechanic, ultimately. Partial application vs structs/objects/tuples are just two different solutions for the same problem. Arguably they may even reduce down to the same underlying solution (to the extent that closures are objects behind the scenes).

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

#392
post #375

Earlier quoted context omitted.

That's actually an argument in favor of linear or affine types. In your example, you'd still get a new Wallet from that function, but the compiler would keep you from accidentally using the old one afterwards. That way, you can't make that mistake, but still get the benefits of immutability.

I will admit that I don't know what "linear types" or "affine types" are. Your answer makes me feel like I am trying to convey an idea for which you have the perfect formalism / meta vocabulary. It might be that objects, or at least the way that I know how to use them in my favorite languges, are just a (potentially limited) implementation of said formalism. I feel that objects offer a flexibility benefit though (whi…

Rust is probably the most mainstream language that uses affine types. Once you call a function that takes ownership of a value (e.g., `drop`), the compiler won't let you use it anymore after the function call.

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

#393

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…

If you’re asserting non-public internals of a class, your testing is wrong in the first place. Assert outcomes, not the process.

That's the whole point. You can't test outcomes if they are dependent on internal hidden state as that affects the outcome.

Whether you make it private or not hidden state leaks into the output since the output is dependent on it.

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

#394

Earlier quoted context omitted.

I think information hiding is the wrong term. It should be hidden for modification, open for inspection. Functional programming naturally promotes this. OO does not. That's my opinion. It's been a useful discussion though so I appreciate everyone's input.

So are you saying you no longer think OO inherently makes things harder to test? I feel like it's difficult to talk about this without examples. The library I like thinking about when I think of passing around in a state variable is the lua C api. https://www.lua.org/pil/24.1.html here is an example. https://pgl.yoyo.org/luai/i/lua_State here are docs for the state variable's type. This is pretty object oriented exce…

Not exactly, I'm saying OO still makes it hard to test because it is an opaque blob of state. It would be really hard to use/test the LUA API for example without knowing it is using a stack underneath the hood. That detail leaks through the API whether you want it to or not. If it was switched to a FIFO queue that would completely change the behavior.

Since using this API makes an implicit constraint that it is using a stack underneath the hood, why not just expose the stack for inspection? What advantage does keeping it an opaque blob have? I agree you don't want code manipulating this stack (although if it is immutable as in FP this isn't an issue), but since the external code already knows it is a stack and relies on that fact then it is part of the public API already.

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

#395
post #327
post #116

Earlier quoted context omitted.

Is that true though? You can pass the function itself. If your interface is a function that takes no parameters and returns an int, just wrap this function with another function that takes no arguments and moves the arguments into a closure. Doing it in a class is forcing these assumptions on your user - that they need this added complexity in all of their uses, and will make for unwieldy code when they don't, or eve…

This circles back to my original argument. Here: def adder(x): def add(y): return x + y return add add_five = adder(5) add_five(10) How is that different from: class adder: def __init__(self, x): self._x = x def __call__(self, y): return self._x + y add_five = adder(5) add_five(10) Did you have something like that in mind? Do I understand your idea correctly? They both allow you to achive the same thing. The syntax d…

The difference I was aiming at is that you (a library author, whether public or internal) shouldn't implement adder, you should implement add. And let your caller implement adder using add, if they require it. If you only expose adder, you limit possible uses for your library.

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

#396
post #151

Earlier quoted context omitted.

Why not make max or pow an object then? If you can model something as a pure function, then it should be a function. You can always add memorization later, if needed. OOP is fundamentally different from FP, objects are not just higher order functions, they are also coalgebras. There is no need to force every peg into a coalgebra hole.

Yes, why not have max and pow as objects? `max` and `pow` can either run eagerly (as functions) or they can run lazily and allow for interesting lazy things. For example, if you put them into a lazy object graph, you could perform optimizing measures. Check this out: 1/2 * 2/1 == 1 Would you actually calculate each intermediate result of that expression? Or would you just reduce the fraction? Should 1/2 be exectuted…

You can get the same effect if you model the expression as a free algebra (ADT). You can then simplify the terms in the same way. The paper: "The design of a pretty printing library", by Hughes, explains this nicely.

The main difference is that algebras are more natural in FP and coalgebras in OOP, but they can mostly do the same things, just a bit differently. Actually you can also do algebras in OOP as well (visitor pattern) and coalgebras is FP.

Basically my point was that you should use the simplest abstraction that gets the job done. Even if you implemented max and pow as objects, for your specific use case, you would probably just call the pure functions inside.

Thank you for the book recommendation.

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

#397
post #371

Earlier quoted context omitted.

Python doesn't have private members, so that doesn't help you as much.

Members in Python are never private. Variables that are supposed to be used internally only are marked with and underscore, but that just conveys intent and isn't enforced by the interpreter/runtime. But you can emulate private data like this: >>> def a(u, v): ... def b(): ... return u + v ... return b ... >>> b = a(1,2) >>> b() 3 Like this, there is no way to access u & v from b.

Looks like classes can't be closures, though:

  >>> def f():
  ...     x = 5
  ...     class Blub:
  ...             def incx(self):
  ...                     x += 1
  ...             def getx(self):
  ...                     return x
  ...     return Blub()
  ... 
  >>> j = f()
  >>> j.incx()
  Traceback (most recent call last):
    File "", line 1, in 
    File "", line 5, in incx
  UnboundLocalError: local variable 'x' referenced before assignment

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

#398
post #241

Earlier quoted context omitted.

You aren't arguing anything, you are only stating a very shaky premise.

Not any less meaningful then your comment. You aren't arguing anything, you are only stating a very shaky premise.

What I said wasn't a premise, it was pointing out that you didn't back up what you were saying with any substance, which can be checked by reading your comment.

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

#399

Earlier quoted context omitted.

> Use objects to encapsulate functionality Sadly objects are also used in an attempt to hide data (because direct access to data is considered dirty in OOP). Now, consider a non-trivial OOP class hierarchy. You have an object that needs to mutate another object's data (an object that is completely unrelated to your initial object). In order to solve this you have options: - completely rearchitect your code so that yo…

OR, don't use a 'setter', use a carefully named API that describes the purpose instead. Or design the two classes to share the data with locks. Or whatever your solution requires. Yes breaking encapsulation willy-nilly is a bad thing. So don't. I don't doubt there are bad programmers out there. Blaming the hammer for a bad carpenter is foolishness. Btw objects are used for like 5 different things. Choose what works f…

> don't use a 'setter', use a carefully named API that describes the purpose instead.

Call it whatever you want, the requirement is for one class to produce a side effect in a completely unrelated part of the code (happens all the time). This doesn't fit with the current architecture, and there was no way of predicting it beforehand. Now what? Rewrite everything? What happens if you're in a department of 30 developers? Tell them "hold on while I go back to the drawing board to create a clean design"?

OOP isn't a hammer, it's duct tape, and carpenters are taught from school that duct tape is the main way of doing carpentry. It's duct tape because it ties data to functions in a way that makes it very difficult to extract or reuse that data somewhere else.

When OOP is your religion you don't blame the bad carpenters, you blame the master priest.

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

#400

Earlier quoted context omitted.

Mutable in C++11 land holds a weird space because the threading model says that const member methods are thread safe, which mutable member variables are not.

> threading model says that const member methods are thread safe that's only true for standard library objects, although it is an useful guideline for all code.

I don't think that's true.

> A C++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function’s non-const arguments, including this

Consider the case where I invoke `std::find_if`. It takes const iterators to a std::vector. I'm now indirectly modifying objects through standard library functions that are modifying objects by multiple threads through const arguments.

Pretty sure this is a viral requirement and using any part of the STL can effectively taint your program if you're not careful.

Post reply on HN