Hacker News new | ask | show | jobs
by sdegutis 2555 days ago
This is really hard to follow and I'm not sure I'm understanding it the way he intended.

Correct me if I'm wrong, but he seems to be saying that, to avoid breaking consumers of your library often by changing the implementation, hide the implementation details behind classes, right?

This seems to be a common reaction to someone who experiments with the "freedom of functional programming" where that freedom means operating on and returning raw data structures that OOP usually hides behind private variables.

That's still bad practice, even in code that heavily uses FP, and good code usually mixes FP and OOP properly, so that you're given functions when you're meant to have functions, and data when you're meant to have data. This is how I've been writing JavaScript for a few years now, and it's not how I've seen Java or Clojure usually written.

1 comments

I don’t think he’s exactly advising any particular action. Rather, data structures and objects tend to be badly conflated, and there’s a lot of value in clarifying the distinction between them. You’ll use each in different circumstances, for different reasons, by weighing the needs of the system against the design tools at your disposal.

In Rust, we keep the same distinction by modeling data structures as structs and enums, and modeling the “object” side by traits (whether static- or dynamic-dispatch). Traits decouple a consumer from the particular data and emphasize a behavioral contract, allowing any data structure to implement the desired behavior.

So basically mixins, right? Those were hard to use correctly in Ruby, because you might have multiple whose behavior clash because they can access the same data and were not written with each other in mind. I wonder how Rust solves that.
Not quite. A mixin is a piece of code written once and transcluded into another module. Traits are more related to OOP interfaces: every type implements one in its own way. The difference with interfaces is that traits can be implemented separately from the definition of the underlying data type, which clarifies the distinction between inherent operations on a specific data structure, and derived operations that bind it to a more general contract of use.
I really liked the idea of traits in Rust - a kind of interfacing you couldn't get cleanly with C++. The early snag in development there was that the public-vs-private issue couldn't be (sensibly) resolved when a trait was inherited from two "directions" (e.g. modules) - one where it was made public and the other private. It's a problem inherited from C++ and any other OO lang that makes the public/private distinction. I'm sure they've picked a default by now (hopefully public?), but I haven't kept up with it.