Live data from Hacker News

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

quuxplusone.github.io

151–160 of 406 posts

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

#151
post #95

Reducing OOP to implementation detail features will yield poor results. The flexibility stems from an improved means of analysis in that types and entities can be identified more easily. By using classes and objects you're retaining flexibility because they can be interchanged; It's easy to create an object that pretends to be a function; it's harder to make a function retain state later. The example is nice; it remo…

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 and yield 0.5 immediately, or could it be useful to have them hang around some more? FP vs. OOP is the difference of understanding / as "Please divide 1 by 2 now" vs "That IS a fraction; one half".

> There is no need to force every peg into a coalgebra hole.

True, but how awesome would it be if I did that anyway :D

Regarding `max` and `pow` as objects: Yegor Bugayenko argues for a similar thing in his book "Elegant Objects".

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

#152
post #49

Earlier quoted context omitted.

Precisely. And that's the way it's done in functional languages.

No, it's done inverted in functional languages! In a FPL your graph will be an ADT. The advantage is clients can pick it apart, but there is no easy way to extend it. If you wanted to "use the same object for computing distances to multiple vertices", you're screwed: the ADT is laid bare, and there is no data hiding. In an OO language, your graph will be an opaque object, and you can write things like CachingDijkstra…

I think using existential types will give you data-hiding like this. (Disclaimer: I'm still learning about them, and making up this syntax.) Say my abstract data type is

    DijkstraLib =
    there exists GraphState with {
        construct: List(Edge) -> GraphState
        getDist: GraphState -> Vertex -> Vertex -> Num
    }
Now we can write two different implementations of this type that use a different data structure for graph. The data structure will be opaque to the client (even though the client obtains and passes around graph objects!). Like so:

    -- Library
    type CachingDijkstraLib : DijkstraLib
    with GraphState = (HashMap((Vertex, Vertex), Num), List(Edge))
    with {
        construct(a_list) = (HashMap.empty, a_list)
        ...
    }

    -- Client
    DL = CachingDijkstraLib : DijkstraLib
    some_obj = DL.construct(edge_list)
    DL.getDist(some_obj, u, v)
    DL2 = SomeOtherDijkstraLib : DijkstraLib
    some_other_obj = DL2.construct(edge_list)
    DL2.getDist(some_other_obj, u, v)
The types of some_obj and some_other_obj could be completely different, and neither would be accessible to the client. For example, the client couldn't assume some_obj is a pair and try to get its first element. It would also be an error to call e.g. DL2.getDist(some_obj, ...).

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

#153

Earlier quoted context omitted.

Let's use C and C++ as an example. What does the code look like? myobj.cool_func(1,2,3); // C++ cool_func(myobj, 1, 2, 3); // C Is this better? If you use a vanilla C++ style class then the class definition is in the header. All changes to the class require a recompilation and break the ABI of the class. This means you basically cannot release patch versions of your library because consumers cannot link against it if…

> If you use a vanilla C++ style class then the class definition is in the header. All changes to the class require a recompilation and break the ABI of the class. definitely not all, only things that change the layout. adding a non-virtual method does not break ABI at all for instance. > Contrast with C where you would forward declare a struct in the header and pass that around while the definition is hidden in a .c…

> only things that change the layout. adding a non-virtual method does not break ABI at all for instance.

Yes, you are correct. The point was that C code can keep the ABI intact through (some) data layout changes but vanilla C++ classes cannot I misstated it.

>Good modern practice (c.f. Rust, etc) is to ship LTO'ed mostly-static binaries.

That is the state of play now because Rust does not have a stable ABI. I thought one might be coming but maybe no one's working on it. It's unfortunate because it would be really nice to have binary crates with signing.

>hopefully in a few years everyone will switch to snap / flatpack / appimage to finally reach a sane application distribution model on Linux.

Snap has a while to go before it can be adopted fully. Images are enormous, they take a long time to load, and they don't respect (or don't understand) hidpi settings. I tried to use it for spotify and telegram-desktop and other applications but they just don't work that well.

>it much harder to hack your way out of things when it's 3AM and everything is crashing and you have 5 minutes to fix things before heads start to roll.

Absolutely but it comes from enormous systems like Windows where changing a library and breaking ABI means recompiling Everything. And these compilation jobs are often 'overnights'.

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

#154

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.

But if you're passing a state object around, you might as well use a class, no? Admittedly simplifying a bit, an instance method is a function that implicitly takes "this" as the first argument.

Why? The main advantage of passing a state as an argument is letting the user control the state. If you need an array as state, the user might want to put the array on the stack, or the heap, or in static memory, or protected behind a mutex somewhere...

It doesn't really matter that much how things are actually implemented: most languages are flexible enough that you can do this with a class or a function or even implement the algorithm as a generic interface. But if you want the algorithm implementation to be reusable, you have to remove as many assumptions about the state as possible because otherwise some users won't be able to use your algorithm.

If you pick up a class, and the class owns the state, now you need to make the class generic to allow the user to customize the state, you need to allow the user to break the state invariants, to be able to unsafely read and write from it, because otherwise you are forcing the user to read into a separate buffer, and then make a copy to your class, etc.

Somebody that knows how to avoid all these issues when writing their algorithm as a class, probably also knows that by just using a function most of these issues just cannot happen anymore (or are much harder to introduce).

You can also write the algorithm as a function, that takes some generic state, and provide a "class wrapper" for convenience, so that those who don't want to customize anything don't have to. But then your class doesn't implement the algorithm anymore, it just wraps it.

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

#155

Earlier quoted context omitted.

But if you're passing a state object around, you might as well use a class, no? Admittedly simplifying a bit, an instance method is a function that implicitly takes "this" as the first argument.

Let's use C and C++ as an example. What does the code look like? myobj.cool_func(1,2,3); // C++ cool_func(myobj, 1, 2, 3); // C Is this better? If you use a vanilla C++ style class then the class definition is in the header. All changes to the class require a recompilation and break the ABI of the class. This means you basically cannot release patch versions of your library because consumers cannot link against it if…

> Is this better?

Very few professional developers code in vim or notepad. Vast majority of us are using IDEs. IDE knows types of things, type `myobj.` and you'll get a suggestion list with methods of that class callable from the current context.

> There is then a pattern to do this in C++ called pimpl

There's another useful pattern in C++:

    struct iObj
    {
        virtual ~iObj() { }
        virtual void cool_func( int a, int b, int c ) = 0;
        static std::unique_ptr factory();
    };

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

#156
post #46

This is why I like languages where you just can't have a free function! I'm arguing the opposite, free functions are an annoying and confusing anti-pattern when dealing with OO languages that allow them. C++ is often just a maze of mostly write only code.

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

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

#157
The standard "right tool for the right job" adage comes to mind. If I write Java or JavaScript, then using objects is fairly important (in fact, I have a hard time writing good JavaScript, because it is really an "object-based," as opposed to "object-oriented" language that rewards runtime modification, and my mind doesn't really work that way).

As was mentioned earlier in the comments, if we are designing code for reuse, then using a reusable design pattern is important. It doesn't have to be a class (I program in Swift, which uses structs and enums more often than classes), but it should be in a form that can easily be extended or derived from.

I will also do stuff like refactor a bunch of code out in a project that I'm developing, and create an entirely new project, based on that, so it can be reused. In that case, I may take a simple, focused tool, and make it a bit more generic and/or complex, widening its utility (of course, that also means that I add a bunch of testing that would not have happened, otherwise).

I sometimes think that we get caught up in the tools or dogma; letting them define us. I say this, having been through exactly that.

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

#158
post #117
post #3

Also known in python as "if your class has only two methods, one of which is init, it's a function" in the "stop writing classes" https://www.youtube.com/watch?v=o9pEzgHorH0 EDIT: typo, changed link

Do python linters catch that? It is not a code error and sometimes not a design error either, but a warning would be useful, and linters may have the tools necessary for that.

It's a warning in pylint by default: "Too few public methods (1/2)"

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

#159

A lot of time where I just write simple functions, I end up having to wrap them into objects because it is much more convenient when I want to do static polymorphism - sure, that function I'm writing now doesn't need state but then two days after I have to refactor because the next strategy I use does indeed require state. e.g. : template struct MyAlgorithm { F1 f1; F2 f2; void operator()(...) { ... f1(whatever); ...…

> you can't just pass functions to F1 or F2, they have to be objects. Why? https://godbolt.org/z/2kJsT9

I'm new to C++, what are lines 11-12 for? I see that it doesn't compile without it. It doesn't seem to be the new 'arrow return type' syntax because there's no auto keyword.

Edit: I found it - it's a 'template deduction guide' https://stackoverflow.com/questions/40951697/what-are-templa...

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

#160

Earlier quoted context omitted.

Let's use C and C++ as an example. What does the code look like? myobj.cool_func(1,2,3); // C++ cool_func(myobj, 1, 2, 3); // C Is this better? If you use a vanilla C++ style class then the class definition is in the header. All changes to the class require a recompilation and break the ABI of the class. This means you basically cannot release patch versions of your library because consumers cannot link against it if…

> Is this better? Very few professional developers code in vim or notepad. Vast majority of us are using IDEs. IDE knows types of things, type `myobj.` and you'll get a suggestion list with methods of that class callable from the current context. > There is then a pattern to do this in C++ called pimpl There's another useful pattern in C++: struct iObj { virtual ~iObj() { } virtual void cool_func( int a, int b, int c…

https://www.cairographics.org/manual/cairo-cairo-t.html

All operations on `cairo_t` have prefix `cairo_`

Post reply on HN