Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Rich Hickey has a great talk about how Objects are data structures with unique interfaces that are unnecessary complexity. He showed and example of a web server with a web request object with request headers etc etc. Doing simple things like collecting information out of that nested object structure is bespoke and harder than it should be for no real gain. If everything is a map or list or set, it becomes completely trivial to extract and manipulate data from a structure. It's a subtle difference at first but when everything is like that it makes your system far simpler. Unfortunately I can't remember exactly where the part of this talk is.



Yes, this hit home so hard. All objects are bespoke mini languages that add little to no value and simultaneously make it hard to remember and hard to use. Just use maps!


An object is a schema, though.

A map is... Whatever you put in it.


This is the benefit. Languages like Clojure have excellent support for manipulating maps and I'd wager that something like 40-60% of Clojure code is similar-looking map manipulation stuff for this very reason.

If you need that validation, you just validate the map? You can use established methods like Malli or Clojure Spec for this. If you need to use a record with a fixed schema, just use a record instead of map. In Clojure, you can use most of the map functions for records too.


This comment is talking specifically in the context of Clojure.

In the Clojure culture (so to speak) maps may also have a schema, as used by various schema checking tools, which are richer than runtime type checks. (Not the same as database schema)

Nit: I would not say that a JVM object is a schema, because there’s more to it. Rich is well known for saying that the idea of an object complects two things: (1) a record-like (struct) data structure with the (2) code to manipulate it.

Sometimes it’s even more complected because in some languages classes can make assumptions about state across all objects and threading.


Constraints always have value. Without constraints programmers are confronted with the naked complexity of mapping between the set of all possible states of N bits which is (2^N)!.


More constraints don’t necessarily have more value, though. Each constraint needs to be thought through, not just have its value assumed.


> Unfortunately I can't remember exactly where the part of this talk is.

If you or anyone else remembers, I'd love to watch


Here is this specific part cut out from the larger talk "Clojure made simple": https://www.youtube.com/watch?v=aSEQfqNYNAc



Most of the time, the class name is used just as a nominal type identifier. That's what we do at least


“No real gain”

I don’t agree with this. iirc Rack ultimately uses and array to represent HTTP responses. It has three members: the status code, the headers, and the response body.

If you’re shipping a new change, is it easier to mistake response[0] or response.headers?

This is a trivial example, but the general class (ha) of trade-off is amplified with more complex objects.

I love clojure and lisp but the blindness behind a statement like “no real gain” has always kneecapped adoption.


> If you’re shipping a new change, is it easier to mistake response[0] or response.headers

False dichotomy. There are many options other than arrays. Clojure in particular is fond of hashmaps. You can have your response.headers without OOP.


In Clojure, response.headers is still data :) You just use the built-in ways of reading named keys, such as (:headers response) or (get headers :response).


Errata: (get response :headers)


I think there is a need for objects. An active connection, talking to the GPU, etc are not data -- identity is essential for their operation.

OOP is probably the best way to model such,.. well objects, allowing them a private, encapsulated state, and making it only modifiable, or even viewable through a well-defined public interface that enforces all its invariants.


I think OOP (object oriented programming) is abused and is not the optimal paradigm for most software services. It succeeds best at providing inter-operability structure for API design. "Objects", as mentioned here, are an abstraction. Stateful data can be organized and manipulated elegantly without use of the OOP paradigm. Many small systems that employ OOP hamper their maintenance and extendability by unnecessary dependence upon encapsulation and data ownership. Mutability is a villain here as well -- when data structures are immutable, there is little fear of panoptic architectures designed without ownership constraints. Here software is no longer corralled into walled gardens of "objects"; large complex types and their brittle method associations are avoided, greatly simplifying software architectures as a result.


Clojure uses objects for connections and things, but POJOs are harmful IMHO because it makes manipulation and collecting data a bespoke task every time. Every time you change the data, you have to change a class to represent the JSON and the ORM class and .... this is all just data


Well, that’s what Java records are for. As for having to change the type description, that’s more of a static typing discussion, though it can be generated — having a single source of truth and generating others is imo the best approach.


Java records weren't even a gleam in the eye of James Gosling when Clojure was solving these problems at its inception.


In his eyes? It definitely was a thing, records are just nominal product types, these are probably the single most used building block of programming languages.

I really like Clojure, but I really don’t know what some of its fans think (also true of other lisps), like there is a healthy pollination of ideas between languages, lisps are not God’s language.


Standard ML had records since the '70s. Both Clojure and Java would benefit from taking more from what came before, though Java at least had the excuse of being designed for low-powered set-top boxes.


According to the link below, ML records are mostly handled by hash-maps in Clojure, except that there’s no canonical key/val order or strict typing by default.

ML record:

  {first = "John", last = "Doe", age = 150, balance = 0.12}
Clojure hash-map:

  {:first "John", :last "Doe", :age 150, :balance 0.12}
Destructuring a record in an ML function:

  fun full_name{first:string,last:string,age:int,balance:real}:string =
    first ^ " " ^ last
(It’s unclear from the example whether or not all of the destructured values are required in the function signature. I hope they are not, but I left them in since I don’t know. The caret positioning raises further questions.)

Destructuring a map in a Clojure function:

  (defn full-name [{:keys [first last]}]
    (str first “ “ last))
I don’t know if I’m missing something that ML offers with its records aside from more strict typing, which you can also have in Clojure when it’s useful. In both cases, it looks like it’s applied at the function declaration and not the record declaration.

https://www.cs.cornell.edu/courses/cs312/2008sp/recitations/...


Clojure has records too.


Connection pools exist precisely because the code outside of the connection management piece shouldn’t have to care much whether or not there is an active connection. It’s all boilerplate, except for handling the “unable to connect” case.

When you call a connection or connection pool object, you’re querying its current state. This is absolutely data.




Consider applying for YC's Winter 2027 batch! Applications are open till November 2.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: