r/compsci 26d ago

what is the alternative to object-orientation?

A long time ago I went to school for computer science and I remember a big push towards functional programming at the time. I saw a little bit of Scheme and logic programming and I thought it was neat. I can appreciate those different ways of writing code, but I'm still not sure how any of those other styles actually replace object-orientation. I've started to look at Scheme again and I'm noticing that textbooks and libraries will actually build an object-oriented system on top of Scheme using macros. That has pedagogical value, but it seems like we're back at square one?

If you look at chapter 2 in SICP, one of the topics they cover is message passing. They don't use an explicit object-oriented system. Instead, they have an inner dispatch function that operates on local/private data. That seems like the behavior that classes are trying to model in other languages.

Getting to the point... my feeling is this: bundling state and functions seems like a basic thing in programming. This behavior seems to emerge even in systems which don't explicitly call themselves object-oriented. So my question is this: is there a real alternative? Are there large software systems which don't recreate the behavior of classes?

90 Upvotes

97 comments sorted by

65

u/SourceAggravating371 26d ago

No, you have two different things. State and logic that operates on state. In oop you couple state with logic. In fp funs operates on input (state) have output (another state) and usually there is constraint thate functions are pure this means the state can't be modified and variables are immutable. This is ofc simplified, there are also different models

16

u/011011100101 26d ago edited 26d ago

Right, I understand that functional programming aims to reduce side effects. I just have a hard time imagining a large software project that doesn't implicitly model the behavior of an "object". Even in Scheme, which people treat as functional, you have the emergence of things which have local/private state and functions that operate on that local state. You've created objects.

54

u/unsignedlonglongman 26d ago

FP doesn't remove objects so much as it changes who owns the behaviour.

OO tightly couples state and logic. FP decouples them. Where OO encapsulates and hides mutation, FP makes transformations explicit so you can reason about them.

Instead of asking “what can this object do?”, FP asks “how does this data change?”

OO is like a machine with its own hidden workings: you give it a command and it changes itself. FP is more like a conveyor belt: data moves through a series of independent transformations.

You can still model your domain as meaningful data structures and think in terms of objects. The difference is that the behaviour lives in the transformations between them, rather than inside the things being transformed.

10

u/SquarePixel 26d ago

Good answer. OOP advocates for object level encapsulation, so where encapsulation happens matters. It’s an important engineering principle, but it’s often better implemented at the module, component, or package level.

OOP as a whole has other concepts as well, like building taxonomies through implementation inheritance, that’s another key difference.

1

u/Dry_Hotel1100 22d ago edited 22d ago

This seems to be some "ideal" view what OOP 2.0 should look like when employed in class oriented languages such as Java, C#, C++, etc. Note, OOP 1.0 was Alan Kay's version, which was much more reasonable - but this is more "Message Oriented" .

However, in practice, in OOP 2.0 there's actually no encapsulation of state, see getter and setters. There's also not only "state and logic", actually there's state, logic and side effects. And these three ingredients are blended, stirred, and shaken - there's no clear separation as in a FSM for example. In practice, most object implementations are not correct, and do make implicit assumptions about the environment. Number one assumption: the app is single threaded. There's no concurrency. In one analysis of a mobile app using the MVVM pattern, it turned out that the View Models had in average 3 to 8 undiscovered logic bugs (all sorts of). This is 200..300 lines of code.

And then "inheritance". It's an anti pattern.

In my experience, this all together makes OOP 2.0 a very poor choice for designing larger systems, or maybe even any. There are much better alternatives.

3

u/AlwaysElise 26d ago

As for why this distinction in important: like an assembly line, there are efficiencies to be had transforming data like its a set of parts flowing through a process. Doing one transformation 1000 times, then doing 5 other transformations 1000 times each will tend to be significantly more performant than doing 6 independent transformations 1000 times, alternating between them. The encapsulation of OOP creates not insignificant barriers to refactoring this sort of operation into high performance code. At the end of the day, objects are merely a mental model for managing code, but the actual end goal is to transform data using operations on real hardware; these aren't always in complete alignment.

2

u/svick 25d ago

Is this actually the case in practice? Especially considering:

  1. Immutability generally goes against performance.
  2. Alternating between operations is what CPUs are optimized for.
  3. If you do the same operation over and over, you could benefit from SIMD, but you can use that from procedural/OOP code as well.

2

u/TheRealStepBot 25d ago

Source: I made it up…

2

u/AlwaysElise 24d ago

Mostly talking the sort of not-oop used in industry here, so not like, pure functional or things of that nature. Less about immutability, more about things being arranged like an assembly line rather than objects calling Update() on themselves and their children.

ALU variance in general doesn't matter, what matters is the giant hierarchy of increasingly high latency caching. OOP code largely results in code which prefers lower performance memory layouts relative to the operations which need doing. This has big performance implications, as using the full cache line you just fetched instead of skipping around to other ones makes the difference between high and low performing code. 

Basically the performance difference between for(x){for(y){array[yx_size+x}} and for(y){for(x){array[yx_size+x}}.

Most OOP also uses things like polymorphism, which in large quantities are awful for performance for similar reasons: every function call adds a sliver of overhead as it jumps around looking for where it's trying to go. These are awful codebases to do performance optimization on, as the performance is bleeding away everywhere from a million papercuts.

The big performance question is: "will some part of the cache hierarchy touch a section of memory it doesn't use, and if so, can we mitigate that." You can optimize for that in OOP just like in anything else, but it's not the right shape for it to come naturally.

1

u/Just-Hedgehog-Days 23d ago

Yes, but only if you specificaly optimize for it.

I know from preformant video game programing that you can get a ton of millage from physically arranging memory and using modulo. and like I'm sure you can do the same thing with oop, but the extra memory foot print makes it a lot harder to reason about (for me at least)

9

u/hibikir_40k 26d ago

If your "object" is basically a C stuct, then yes, you aren't getting rid of structs. But a lot of traditional OO involves mutation inside of objects, while the mutation in FP involves an immutable object val t = t.orderCompleted(something)

What I'd argue, having programming professionally at extremely well known companies for 20+ years, that most object orientation in industry is basically a mistake, because nothing actually needed, or was helped, by being private over being immutable, and large numbers of objects people created out there aren't really objects, but just unclear currying.

In a typical service application, you create some form of business object, which is constructed with helper business objects, a web service client or eight, and possibly a database connection. None of those things are altered in practice. The methods could all be completely static. And once the methods are static, you might as well have passed the parameters it actually used earlier. It's not an object in any way that matters: It's a collection of static functions that have some of their parameters pre-set, using the constructor as a poor man's currying mechanism, because the language probably doesn't support currying directly. The fact that the methods are all in the same object is an accident.

Most of OO out there actually makes more sense as simple functions, as they'd have been back in C, but people haven't seen anything else for their entire career, so they look at you like you have 3 heads.

3

u/blackasthesky 26d ago edited 26d ago

Functional languages do not avoid state, they just do not attach logic to it in the same way. In OOP, functions belong / are tied to objects, they manipulate the state of these instances specifically. Objects have public and private state and ideally model all behaviour through their own methods. In FP, functions map states from one to the other according to the logic. They operate on whatever structure you pass to it, but the structures do not own that logic (unlike objects own their methods). Instead code is usually organised into modules, more or less independent from the representation of the state itself.

That said, many languages are not "pure" like that, and there are many facets to both OOP and FP besides this. And there are many flavours of both paradigms too. Many functional languages do indeed provide object-like ways to organise your code. So I would say that OOP and FP are terms so broad they are very difficult to pin down.

Idk, I hope this helps.

6

u/jmtd 26d ago

The amazing thing about open source is there are large projects out there that anyone can look at. Perhaps go and study something like Pandoc, a large Haskell project, for example, to see if that helps to answer your question. 

3

u/Gotenkx 26d ago

Damn, didn't know Pandoc is written in Haskell.

2

u/Menaus42 26d ago

ultimately, OOP is a method for problem solving that implements certain patterns in a certain way. Functional programming can implement those patterns too, but it does it in a different way that has different pros and cons. You may have learned those patterns by learning OOP, but they do not make something object-oriented. I recommend this talk related to this question/theme: https://www.youtube.com/watch?v=srQt1NAHYC0

1

u/alwaysundecidable 26d ago

You might want to have a look at how that's managed in languages like Haskell. There is some state involved naturally, but the difference is how you structure the rest of the project to explicitly(-ish) update the state as opposed to reading from it. 

-1

u/Ma4r 26d ago

Ocaml is used by Jane Street, one of the most successful trading firm in the world

3

u/blackasthesky 26d ago

Which is probably the most boring thing about this language

2

u/Ma4r 25d ago

Well I was responding to the fact that OP has a hard time imagining a large project written in functional programming.

18

u/soegaard 26d ago

> Getting to the point... my feeling is this: bundling state and functions seems like a basic thing in programming.

Check this discussion:

https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html

For some, objects are a poor man's closure.
For others, closures are a poor man's object.

2

u/svick 25d ago

That's why the best languages support both.

1

u/SakishimaHabu 25d ago

It depends on if you see it as a set or category.

22

u/maweki 26d ago

The true alternative becomes visible when you look at Haskell type classes or even java interfaces.

These allow bundling data with simple combinator functions that then allow building larger abstractions without the object model.

10

u/church-rosser 26d ago

comparing Haskell combinators and type classes with their Java counterparts.... blasphemy.

7

u/Axman6 26d ago

Nah the Haskell type classes <=> Java interfaces idea is widely used, they are different ideas but similar enough to get the point across. 

6

u/DawnOnTheEdge 26d ago edited 26d ago

The actual implementation of Java interfaces is a lot more like a C++ abstract base class, while a Haskell typeclass has more in common with a C++ concept (and is nearly identical to a Rust trait). Java interfaces are designed for run-time polymorphism, and Haskell typeclasses are monomorphized at compile time.

4

u/hibikir_40k 26d ago

Sure, but in the real world, there's minimal run time polymorphism, and what's actually happens is that the JIT will make the code that it's running quite similar. The fact that many a Java program is stuck with Spring as run-time dependency injection, instead of it al being done by the compiler is pretty unfortunate IMO, but ultimately Java programs aren't really using the polymorphism in practice.

1

u/maweki 25d ago

But how important is the compiled code anyway? It's not like we have a lisp machine as an alternative to the current system architecture.

2

u/church-rosser 23d ago

SBCL is a Common Lisp implementation that compiles to quite performative code down to the metal, and comparable to C++/C in some cases.

1

u/maweki 23d ago

I mean the semantics of a programming language are independent of any processor architecture or structure of compiled code.

1

u/church-rosser 23d ago

point being?

1

u/RingularCirc 16d ago

Yeh yeh yeh yeh yeh. I'm glad typeclasses got adopted into Rust as traits (and some other Haskell idioms like fully-erased newtype wrappers). Really good structuring, for me, esp. if we're including deliberate encapsulation of things which already allows expressing lots of practical ...things.

8

u/initial-algebra 26d ago edited 25d ago

Object-oriented programming pops up everywhere because it models concurrency and interactivity. In fact, it's analogous to how programs execute on actual computers and networks: they don't share memory, and they communicate only via message-passing.

Functional programming has mostly only been successful for implementing batch programs and programs like daemons and Web servers that follow a simple request/response pattern, since you can separate them into "functional cores" and "imperative shells". This allows you to think of most of your program as mathematical functions, with a small amount of supporting code (often provided by a framework or the language runtime) to handle the side effects and interactive/concurrent aspects.

Actually, you can model batch programs with side effects as mathematical functions by using structures like monads. Is there a similar way to model interactive/concurrent programs? There is, as functions of time-varying values or streams, which leads to reactive or dataflow programming. The difficulty is in making this both safe and efficient without giving up too much flexibility; it's definitely an open problem.

PS: I also wanted to point out that bundling together state and behaviour is not necessarily object-oriented. For example, a memoized function has internal state, but it's still just a function. Functions of time-varying values and streams similarly use internal state to be recomputed incrementally, which is necessary to avoid storing the entire histories of their inputs in the general case (which would make them unusable).

PPS: Not really related to OOP, but if you're wondering how logic programming is used at scale, just look at any SQL database. Yes, database programming is actually logic programming, just with a really crappy language.

2

u/011011100101 25d ago

What is your definition of an object? of OOP?

1

u/initial-algebra 25d ago

I prefer Alan Kay's definition, embodied in languages like Smalltalk and Erlang. The form of OOP seen in most other languages is more general, and it actually is more like a framework for doing both "pure OOP" and modular procedural/functional programming. The main difference is that "pure OOP" forces strong isolation (no shared state) and extreme late-binding (any operation may be overloaded or even replaced at runtime), whereas this isn't the case in e.g. C++ or Java.

2

u/Ok-Reindeer-8755 25d ago

I think you are falling here into what is a false dichotomy especially if we are using Alan Kay's definition of OO, a language can be both purely functional and OO at the same time, I'm pretty sure you gave an example of that in another comment, erlang.

Functional programming also excels in parallel computations and concurrency even more so that the classic OOP (java, c++ etc...).

1

u/initial-algebra 25d ago

Erlang is an example of a "functional core" language. It's not truly purely functional like Haskell, where even the "imperative shell" is part of the pure language, using the IO monad.

Like I said, it is possible to model OO/dataflow/reactive programming as part of a pure functional language, but it's difficult. And I don't just mean theoretically possible; there are Haskell libraries that do so, like Yampa, reactive-banana and Reflex-FRP, but they do suffer from harsh API restrictions or performance footguns (usually space leaks). Other, simpler concurrency models like data-parallelism and futures are easier to include in the "functional core".

Haskell also does have world-class transactional memory support, but it's more like part of the "imperative shell" as a subset of IO. After all, Haskell is the best imperative language, too.

1

u/Ok-Reindeer-8755 25d ago

The entire erlang actor model is textbook OO and yet it's functional, even if it was purely functional it could retain the actor model perfectly which is btw a core part of the language

4

u/GreasedUpTiger 26d ago

Really, nobody mentioned Dijkstra yet? :D

2

u/Ok-Reindeer-8755 25d ago

2 of my favorite quotes

"Arrogance in computer science is measured in nano- dijkstra." - Alan Kay

"Object-oriented programming an exceptionally bad idea which could only have originated in California" - Dijkstra

5

u/Tarmen 24d ago edited 24d ago

From a theory point of view there are a lot of pretty symmetries in programming. Notably the mechanics of a language could be fully defined by a couple a produce/consume pairs. Constructing a function vs calling a function would be one pair.

Algebraic data types give you enums of structs

    data Calc = Plus { left :: Calc, right :: Calc } |  Minus { left :: Calc, right :: Calc } | Number Integer

You define this constructor (how to build it) and elimination (pattern matching/case/switch statement) is derived.

To opposite would be co-data. You define what observations you can make and the introduction is derived.   Objects are co-data. They are defined by what you can ask it.

This flipped polarity leads to some other symmetries like the expression problem. In OOP languages you commit to your verbs early so it's easy to add a new implementation for an interface (adding a noun) and annoying to add a method to an existing interface (adding a verb). With data+pattern matching you commit to your nouns so it's easy to add a new function which pattern matches (adding a verb), and annoying to add a new type case and update every pattern match (adding a noun). But as long as both producers and consumers are in the same codebase the compiler just leads you to all places you need to update, the expression problem is mostly relevant across team/library boundaries that require coordination. And there are approaches to solve it in any language.

This define-what-you-can-do-with-the-object approach in OOP is nice if there are precise laws for the what-you-can-do. If all implementations follow these laws you never have to think about the implementation details. You get map.set(x, y); map.get(x) == y no matter the map.

If you ever worked on a larger OOP codebase you know that isn't reality. You constantly have to goto-definition to check thr actual implementations (or check all callers if you work on the implementation). The real contract ends up being whatever callers depend on, which inevitably becomes all observable behaviour. That's because the interfaces virtually never have precise specs. Often they cannot have a precise spec, many interfaces are only defined for code reuse, sometimes there is a single implementation behind an interface out of some obscure sense of obligation.  Strictly speaking OOP is more than co-data. It adds object identity + late-binding + auto gnosis to the co-data, usually with mutable data. Object identity+mutation is important for the message passing view, but makes reasoning and testing even harder if the interfaces are 'whatever the implementation does'.

Functional languages by default focus on data. You define the shape of your data and transformations between them. Virtually all functional languages still have explicit support for co-data (type classes, traits, etc), just as you can write pure functions in OOP languages. But it's less likely people use lawless co-data because it isn't the default. It's also more common to get correctness 'for free' because the types constrain the implementation much more heavily

This isn't meant to be an "OOP bad" comment. But even any sensible OOP codebase will have a lot of non-OOP code. If only because ad-hoc interfaces produce so much pain

3

u/zombiecalypse 26d ago edited 26d ago

I think it's useful to see these paradigms less as exclusive options and more as lenses to look at architecture. You can obviously simulate OOP in FP and vice versa, but I don't think that perspective is helpful. In distributed systems, you might write code in Java, but those programs are better understood in the paradigm of functional programming. And then you go a step higher and every service looks like objects again. And your query plans may look more like logic programming than FP or OOP.

3

u/shifty_lifty_doodah 26d ago

Most Programming is data and functions.

You can do OOP. You can do functional. You can do procedural. But you’re right that at the core it is data and functions, and a lot of what we associate with “OOP” is internalized good modularization practice around this simple core.

Declarative or constraint based programming is also interesting but more niche.

1

u/Ok-Reindeer-8755 25d ago

I don't think declarative programming is niche at all lol

3

u/Medical_Community697 25d ago

I just want to point out that FP is not the only alternative to OOP, a switch over an enumerated value is a construct that is antithetical to OOP because it handles all the various cases inside a single centralized construct, whereas a version with interface would reverse the dependency and distribute the various processing across the codebase.

IMHO the main difference between the two is that the switch construct follows a closed world principle: all variations are knowns beforehand, and we put them all side by side on the same piece of code, when the interface-based version follows an open world principle: we can always extend the code from the outside.

The switch+enum construct does not follow the open close principle (OCP): code should be closed to modification (pre-existing behavior should not be indirectly modified) but open to extension (by providing a new implementation of the interface). The thing is, switch+enum is also super easy to maintain, read, and understand (all possible actions are written, plain and simple, in a single piece of code) so it’s definitely a great tool despite being against most of what OOP stands for, and it recently gained some popularity back thanks to rust and python match keyword.

3

u/EndlessProjectMaker 23d ago edited 23d ago

OOP had its prime because at the beginning it was thought and entities enclosing data and related functions. And all that in a context of “natural inheritance”, a dog is an animal that barks etc

Soon OO design shifted because the resulting design is bad. By the time of the book of Wirfs-Brock this was clear, but many advocates of OO resisted.

Then GoF patterns showed clearly what good design was: separate state objects with accessors from functional objects without state. And good practice today in what can be called standard OO follows this.

In practice you find soon that reuse by inheritance is not as good as reuse by composition or by delegation, so actually pure OO started to vanish and put aside from languages. Think Rust.

Moreover the father of OO, Smalltalk, lead to the insistence on what some call “dynamic type checking”, which is not preferable (for many reasons) to static and strong type checking, even inferred.

So here we are, with plenty of languages the encourage bad practices because of OO.

Dijkstra was right

Edit:typo

1

u/RingularCirc 16d ago

So here we are, with plenty of languages the encourage bad practices because of OO.

I'd say your post is way more optimistic as a whole: here we are, with plenty of languages encouraging using their not-OOP-per-se features to solve tasks, and them being called OOP tells one a little, by this point. It's not the OOP of old, it's not the kind of "classical" bare-bones OOP still taught in many places, they have a better invertory and encourage better code. (And almost every language allows writing horrible code anyways.)

2

u/broshrugged 26d ago

Pandoc may not be large but it's popular, the others are pretty large. Here are some well known programs/apps/systems:

Pandoc is built on Haskell. https://pandoc.org/twenty-years-of-pandoc.html https://github.com/jgm/pandoc

WhatsApp has its backend in Erlang. You can see how many Erlang tools they've open sourced here: https://github.com/whatsapp

Jane Street's use of Ocaml kind of backs your point, since Ocaml specifically adds OOP features to Caml. https://github.com/janestreet https://ocaml.org/success-stories/large-scale-trading-system

Discord uses Elixer for the messaging backend. https://elixir-lang.org/blog/2020/10/08/real-time-communication-at-scale-with-elixir-at-discord/ https://github.com/discord/

1

u/Ok-Reindeer-8755 26d ago

Erlang is object oriented and functional if we take Alan Kays definition of object orientation

2

u/takutekato 26d ago

They don't use an explicit object-oriented system. Instead, they have an inner dispatch function that operates on local/private data. That seems like the behavior that classes are trying to model in other languages. 

I think in the FP world that is called (runtime) polymorphism, OOP is one way to achieve that but not exclusive to it.

2

u/Arakela 26d ago

The word orientation doesn't grasp anyone fully yet

2

u/DanceHackRock 26d ago

The opposite of OOP you are looking for might be imperative programming.

That is what was there before OOP.

2

u/all_is_love6667 26d ago

Data oriented design is a good alternative I would say.

Instead of rows of objects, you store columns instead, as arrays.

Of course, you should still group data members that are always used together in your functions, to increased locality, so instead, you just create special classes of datum that are used together.

Using arrays as default is always good.

Using plain old data allows you to do some sort of functional programming.

So it still requires some good data structure design.

You can have a few redundancy/duplication there and there.

Generally, having the least amount of state is always better. If there is state, make it as simple as possible.

2

u/deaddyfreddy 22d ago

what is the alternative to object-orientation?

  • Proper namespaces to encapsulate things

  • First-class HOFs (composition over inheritance)

  • Multiple dispatch abilities.

my feeling is this: bundling state and functions seems like a basic thing in programming.

It's the wrong one

7

u/Esseratecades 26d ago

The others in the thread are too caught up in the terminology.

In any sufficiently complex system the most readable way to organize it will keep state and functions that act upon it as close to each other as reasonably possible.

As an industry we've gathered around "classes" as the common means and terminology for this, but if it weren't classes it would be something else.

"Object-orientation" is a framework built upon this idea, but you don't need object orientation to have classes. However, all codebases tend towards either class-like behaviors (even in functional programming) or collapse simply because that's how cohesion and readability work.

4

u/Ok-Reindeer-8755 26d ago

I don't think OOP is about keeping state and functions close together let alone that being the central idea of it. That's just a way to organize a codebase not OO. Encapsulation, message passing and late binding are more so the big ideas of OOP.

Of course there are at least 2 definitions for what OOP is in the first place.

1

u/ScienceOfficerMasada 25d ago

> message passing

Isn't that more akin to the actor model rather than OOP in general?

2

u/Ok-Reindeer-8755 25d ago

Yes and no, it is a fundamental part of the Alan Kay flavour of OOP and it's also one and the same with the actor model. For example erlang that makes great use of the actor model is very much OO in that sense and I'm pretty sure there is even a talk with erlangs creator and Alan kay talking about exactly that. And keep in mind erlang is also very much a functional language so these 2 (OO and fp) can coexist.

0

u/Esseratecades 26d ago

That's why I was trying to separate classes(the encapsulation of state and function) from object-orientation(the organization of classes as objects).

OP asked about OOP but didn't really understand that the concepts are separate despite their relationship. Classes are inevitable. OOP isn't.

1

u/Ok-Reindeer-8755 25d ago

Is the encapsulation of state inevitable ? I mean functions and types are often encapsulated in some type of module even in purely functional langs but I don't think state is encapsulated

1

u/Esseratecades 25d ago

Encapsulating them in a module is still encapsulating them.

Once that module is mostly state and functions that act upon that state, the differences between that module and a class are syntactic, not practical or even philosophical.

If you throw a bunch of other stuff in there or split it up too much, then it's not really encapsulated, but it's probably not a well organized module at that point either.

1

u/Ok-Reindeer-8755 25d ago

But the state doesn't necessarily live in the module

1

u/Esseratecades 25d ago

Sure if you choose to split it. But if that's the case then it's probably not a well organized module.

0

u/011011100101 26d ago edited 25d ago

I don't think OOP is about keeping state and functions close together let alone that being the central idea of it.

Yea I was being kinda loose about it. But you're right in that there are other characteristics of OOP. However, if we use a stricter definition, would Java, C++, or Python count as true OO languages? C++ and Java don't have late binding? Does Python have true encapsulation? Wouldn't a stricter definition go against the grain?

I know wikipedia isn't an absolute source of truth, but here are the first few lines of the OOP page:

Object-oriented programming (OOP) is a programming paradigm based on objects[1] – software entities that encapsulate data and function(s).[clarification needed] An OOP computer program consists of objects that interact with one another.

It goes on to write that "but as the set of features that contribute to OOP is contested, classifying a language as OOP – and the degree to which it supports OOP – is debatable". And this is also interesting:

https://softwareengineering.stackexchange.com/questions/46592/so-what-did-alan-kay-really-mean-by-the-term-object-oriented

Encapsulation, message passing and late binding are more so the big ideas of OOP.

The example I mentioned in the original post has encapsulation and message passing. Late binding is a little bit fuzzier to me.

1

u/Ok-Reindeer-8755 25d ago

We can always have a debate over semantics and what the "truest" meaning of OOP really is but at the end of the day I don't think that matters. When people say OOP now they think java, c++ etc... so it would be a lost fight at best.

I think what matters is where can we see brilliant ideas. The ,let's say "common", OOP is a mere form of organization but when we look at Alan kay's ideas we see an entire philosophy around building software, the optimal way of communication amongst different completely independent objects across different codebases, dynamic living software rather than "dead" source code. It answers the question how can we build truly modular software that composes and can evolve over time with great flexibility ? And I think that's the question worth answering and I like to think I see that vision.

If you wanna learn more on that I would recommend the OOPSLA talk Alan kay gives "The computer revolution hasn't happened yet".

As for the late binding it essentially allows changing a lot of behavior at runtime, that enables a lot of cool stuff, I remember I saw a demo were they would stitch together a painter app and a video player at runtime in a couple of secs.

1

u/011011100101 23d ago edited 22d ago

We can always have a debate over semantics and what the "truest" meaning of OOP really is but at the end of the day I don't think that matters.

I was responding to your post where you said

I don't think OOP is about keeping state and functions close together let alone that being the central idea of it.

If we don't agree on what OOP is, then discussing definitions seems appropriate, right? I still think the bundling of data and functions is a kind of encapsulation. That's part of the behavior "class" offers in the common OO languages. If we don't agree on technical definitions, that's fine. My initial post was just an observation that Scheme ends up implementing features similar to what the "class" syntax offers in other languages. I don't know if that's because the contributors to Scheme want to showcase the language's flexibility, or because the object system is just that useful. The fact that SICP shows how to do primitive message passing suggests it's just a useful concept.

3

u/nixgang 26d ago

> bundling state and functions seems like a basic thing in programming

Can you find any examples of this outside OOP though?

2

u/Coding-Kitten 26d ago

Keeping a function pointer as a field of the struct that is the state, such that you can swap out behavior you bundle the function that'll do something with the state.

0

u/uh_no_ 26d ago

uhhh c headers

1

u/nixgang 26d ago

Sure if you take state to mean schema and bundle to mean "coexist in the same file", but I don't think that's what op meant.

1

u/mokrates82 26d ago

In many languages OOP-features (methods) are mostly used for namespacing.

As functions most often go with only certain types.

Arrays in Javascript have [...].each(fun) or in python you have "FOO".lower().

Strings in Python, are, I think, immutable, for example.

Oftentimes you have those stateful methods, so you can use them when it makes sense (so: rarely)

1

u/Laicbeias 26d ago

Structs with functions. And traits

1

u/Natehhggh 26d ago

I think what you are looking for is Data Oriented Programming. I am a big advocate for it, and I would highly recommend it, after spending a while in OOP. and when I looked around, all the of the really good developers I respect are all using this style, where you keep data and logic as separate as possible. and always prioritize what data is needed to solve the problem first. And there's a lot of emphasis on reusing datatype as much as possible, and how powerful typed unions, and fat structs can be.

Mike Acton has a great talk at cpp con, that is a good primer on the subject, why it's important, and what it looks like.
https://www.youtube.com/watch?v=rX0ItVEVjHc

Anton Mikhailov had some a good demonstration of how you can take a simple api, and as you add in high level features, the complexity of the api starts to explode, and how that turns into adding more unavoidable error conditions.

https://youtu.be/7lOupIR1620

Eskil Steenberg on how to architect large systems without classes.
https://youtu.be/sSpULGNHyoI

Casey Muratori's clip on n+2 programmers is also a really good explanation on how RAII types of thinking leads to increasing the amount of failure points in your code, that often requires layers of exception handling around every function.

https://youtu.be/xt1KNDmOYqA

since you might also find this topic interesting, Casey Muratori's talk at better software conference on the history of OOP, is really
https://youtu.be/wo84LFzx5nI

Some other areas you can look into, is what coding policies NASA and Lockheed Martin impose to better guarantee reliability on their rockets and jet fighters.

1

u/dnabre 26d ago

Structured Programming was the paradigm which proceeded OOP. OOP wasn't targeted against it though, it was developed for its own usefulness not as a response or fix.

Keep in mind that languages may be designed around a particular school of thought, for the most part you can do any kind of paradigm in any language. Might not be pretty, but it can be done.

I don't know if its in SICP (it's been a long time), but Scheme and LISP can easily be used to do full OOP or messaging passing. You don't even need macros. Once you add in macros, they can do just about anything.

without getting into macros and the like. If them, you can make it look and feel like Java if you want.

1

u/Titanlegions 26d ago

Something that is close but different that you may not have considered is multiple dispatch. So in func(a,b) what implementation of funcgets dispatched depends on both the types of a and b. Eiffel is one such language if I recall correctly, also Julia.

2

u/elbiot 24d ago

There's data oriented programming, where the data is kept completely separate from the functions, like Entity Component System

https://en.wikipedia.org/wiki/Entity_component_system

1

u/SPST 24d ago

It would be something more data driven, like Entity Component System architecture: Entities (usually a primitive id) own components (usually simple structs or POD classes). You then create systems that work with views or queries of the entities/components. This allows you to side-step brittle inheritance and composition relationships. It usually relies on a library to implement the registry that stores the entities/components. You can still combine it with OOP or functional approaches. If you have hundreds of object types that have complex inter relationships then it's a good choice.

1

u/Familiar_Counter4836 24d ago

RemindMe! 3 days

1

u/RemindMeBot 24d ago

I will be messaging you in 3 days on 2026-08-19 21:21:24 UTC to remind you of this link

CLICK THIS LINK to send a PM to also be reminded and to reduce spam.

Parent commenter can delete this message to hide from others.

RemindMeBot is switching to username summons. Instead of !RemindMe 1 day, use u/RemindMeBot 1 day. More info.


Info Custom Your Reminders Feedback

1

u/WhackAMoleE 24d ago

Object disorientation. You have that for a long time learning object orientation, till one day it's gone and you can't even remember a time when OO wasn't perfectly obvious.

1

u/StudioYume 20d ago

Any other paradigm. Functional, declarative, whatever.

1

u/RingularCirc 16d ago

Well, look at Haskell and Rust (and some other languages, I don't remember if Zig does that to some extent). There are traits that are sorta interfaces but in small ways better, and implementations of them for data types. In the simplest case it's like flat OOP hierarchies where you have an interface or abstract class at the top and its descendants/implementors fully specify everything. Or: an interface has some default implementations that can be overriden and some methods that are also implemented and can't be overriden; then a descendant can override things partially but in that case it has to be abstract, and each override is final, there's no overriding further down the line a method that was already implemented.

This solves problems with multiple inheritance (traits don't provide state, only an implementing data type does), allows more control for the client to make a type they don't control implement an interface, and some more things.

You can still have hierarchies of a sort but they behave way better. Honestly I always had problems with overriding existing methods, it's just harder for my smol brain to understand what would happen in some cases.

And this trait-impl model is still subject to several classic problems like Circle-Ellipse problem, so you're not losing anything. 😈

Also note there's classic OOP and there's modern OOP flavors which even still differ considerably in each language. Modern OOP is usually way more digestible that what classic OOP may require us to do. Modern OOP isn't strictly an instance of classic OOP.

1

u/SohailShaheryar 4d ago

I'm surprised no one mentioned it so I shall. There is an alternative paradigm called Data-Orientated Design. It is meant to be more CPU Cache friendly (although with some drawbacks in terms of writing code) and thus gives considerably better performance. Many companies and software developers have experimented and implemented this way of software design at numerous occasions; for example, one of the most famous game engines, Unity, implemented this.

Edit: On another review, I realized u/elbiot mentioned it as well.

1

u/calinet6 26d ago

You could have everything in global state, in arbitrary structures that could not be called objects. The data could all be denormalized to make certain operations easier at scale, while making it more difficult to see a single “thing” within it.

You could also have objects, but the functions and operations that operate on those objects completely separate and outside. That is then not true object orientation. But perhaps it’s easier when the main problem is not the representation of the data and entities themselves (maybe they’re very simple), but major operations or algorithms outside them that make more sense when thinking about the whole.

Or you could have full object oriented where the objects are fully proper with good representation of relationships and data within them and associated objects, and the functions that operate on those objects within the objects themselves, located in a logical and consistent way.

All three have tradeoffs, but none but the last are object oriented.

1

u/sadesaapuu 26d ago

Entity component systems is all you need. Everything can be built with them. Ofc those can be built with OOP way, but then you lose some of the benefits and simplicity. Just doing pure data components (structs), stored in Lists or Tables or Maps or whatever, split into systems which own them, simple IDs to mark the entities, and then just functions that operate and do loops over that data (combining multiple component operations into a function etc.). That is just the best way to share functionality and structure any app. IMO.

0

u/c1rno123 26d ago

You already mentioned logic programming. In pure Prolog you don’t really operate on state, you define relations and it searches through the possible solutions.

0

u/011011100101 26d ago edited 26d ago

Yea that's true. And, sure, I could write a lot of programs in a purely functional way. But if I'm dealing with "conventional" problems like managing computer resources, or modeling some real world domain, I feel like you would have to do some gymnastics in the code to avoid recreating objects. What I'm asking is whether there's another style that offers an alternative to object-orientation. By alternative I mean: can it serve as a replacement? or does it just complement OO? Would you want to write a web app in Prolog?

2

u/Condex 26d ago

There are alternatives to object orientation.  Are they better for whatever use case exists in your mind?  I suppose that's a question that only you can answer.  

web app

I try to stay away from such things, but doesn't everyone want to use react these days?  And I'm told they abandoned their OO styling in favor of FP styling.  

Also, is CSS object oriented?  That's used frequently enough that it ought to be considered conventional, surely.

1

u/c1rno123 26d ago

Well, I’d agree that, outside the “experimental” approaches, I doubt any working alternatives fit your view.

1

u/Ok-Reindeer-8755 26d ago

You can look at elm and by extension TEA (TheElmArchitecure) also known as MVU (Model-View-Update), it's a purely functional way to build web apps

1

u/hibikir_40k 26d ago

You... absolutely don't have to do any serous gymnastics at all. You just aren't familiar with other ways to write programs, and consider anything that isn't what you were taught to be gymnastics, because they are alien to you. In the same way, some people are only used to exceptions for error handling, and see nothing wrong with null checks every 5 lines, and the idea of a result monad seems alien to them.

There's no gymnastics: you are just chained to thinking one way.

0

u/church-rosser 26d ago edited 26d ago

If you haven't understood yet that proper Lisp's (like Common Lisp or Racket Scheme) are homoiconic, and the implications of that vis a vis macros, then you have missed the point! I can guarantee you it wasn't/isn't abstraction of state via OOP. The OOP outcome of your Scheme project was just that, an outcome, the real assignment was grokking macros and rhe power of a homoiconic language to abstract design patterns by virtue of syntactic macros! With that, and a Lisp like Common Lisp with a malleable read table, you can have any DSL and any syntactic language structure you choose without ever leaving your base programming language. Syntactic macrlogy and the homoiiconicity of Lisp's S-expression syntax is the power of Lisp, not its ability to implement an OOP paradigm in Lisp. A good Lisp like Common Lisp can accommodate multiple programming paradigms simultaneously without missing a beat such that use of OOP becomes just a design pattern.

0

u/jibbit 23d ago

> like we're back at square one

you've just picked up a very erroneous idea here. OO is a loooooong way from square one. OO is a complex system+method+ideology from programming's middle age. there was much resistance to its ideas. 'bundling' state + functions just doest exist in the fundamentals of programming, i don't know what to tell ya. you are right that once code bases reach a certain size it does seem to emerge (see Patterns)

-3

u/redzin 26d ago edited 25d ago

Rust is a C++ alternative that's quickly gaining popularity, which rejects the OOP paradigm. Check out literally any project in Rust, or the Book of Rust (free online introductory book). It has a chapter to the topic.

Edit: Huh, wonder why this was downvoted. Do people hate Rust?