Voted this up out of pure nostalgia. I remember the "spirited" usenet discussions we had with topmind/tablizer on comp.object. He was a one-man army fighting proponents of OO. There were actually some decent ideas buried in his ramblings about table-oriented programming but he was so abrasive nobody could deal with him. I'd be curious what became of him...
I remember him from the late 1990s, I think. One of the various usenet weirdos I have somewhat fond memories of reading at the time. "Wow, who the hell were those freaks", I'd think, as I left a computer room at university, blinking at the sunlight (since the room I'd usually go to, on account of its good-sized set of underused HP9000s, had slightly dim lighting and zero windows), having just spent half a day of one of what would have been potentially the better years of my life reading what these people had to say. How could they find the time, I'd wonder. Didn't they have anything better to do? ;)
Still fascinating though. Usenet was like a crash course in seeing how people can be dicks. Still can't decide whether I regret it or not.
I REMEMBER THAT. I was in my early 20s and Usernet went from heaven to hell in about 6 months. AOL AND NetTV consoles.
Room titles went from Nerd/Hacker/CS to Single Meetup rooms, PR0N and Music topics. I left maybe just in time and moved to IRC (A-Net was also HELL the 4-chan Grandfather)????
I think topmind/tablizer has been somewhat vindicated with the rise of Clojure and Rich Hickey ranting on OO in a much more eloquent way.
But generally, I think the glow of OO as some kind of awesome panacea for software development woes has worn off. Or at the very least, the way it was sold back in the 90s has been pretty much debunked.
I remember him as well, he didn't understand OO well enough to critique it so very little of what he said had any actual value. "Spirited" is a nice way to put it, a bit colored by nostalgia though. His idea of programming is a big ball of procedural mud over a relational database where every task is a hand written sql query/loop over result set, fucking terrible.
Many integrated CRUD apps are best written as procedures (call them REST endpoints if you prefer) that (ultimately) use SQL queries to update a store of facts. In a heterogeneous world, the idea that you have an authoritative model defined in any one language, that merely uses the database for persistence, is untenable. Objects with hidden state are a poor way to model facts about the world.
Having been a follower of OOP since the early 90s until my disillusionment in the mid 00s, to my mind only a handful of problem domains are best modelled as interacting graphs of mutable objects, and the set grows smaller over time. I'm increasingly unconvinced that procedures+SQL done well is inferior to the average developer's OOP code. And if OOP hasn't delivered that kind of mental leverage, then it's mostly a sham.
(I should add that I'm far more inclined to try and model things functionally, with functions that turn input into a set of logical diffs rather than straight-up database modifications, than actually advocate all-out procedural programming with raw SQL. But even raw SQL is superior to effectively performing complex queries in a non-relational language, where the indexes are built by hand and passed around.)
Sure, but the world is bigger than CRUD apps, and even in CRUD apps, object mapping is more productive than hand written SQL slinging result sets around in procedural code. In particular the ease at which OO enabled pluggable programs is invaluable in customizing in different ways for different clients or aggregating data from multiple sources. OO isn't about modeling facts about the world, it's about modelling behavior; objects aren't just structs with some data.
> But even raw SQL is superior to effectively performing complex queries in a non-relational language, where the indexes are built by hand and passed around.
I think that's a bit of a strawman, that's not what OO is.
> In a heterogeneous world, the idea that you have an authoritative model defined in any one language, that merely uses the database for persistence, is untenable.
CRUD apps are mostly about synchronizing a set of facts about the world with the world itself.
Re complex queries in a non-relational language: I know that's not what OO is, but if you're trying to perform a map / filter / aggregate operation across several different collections (sometimes these kinds of things need to be done!), OO doesn't give you many tools, and the tools you can build are usually poor versions of what's available in functional languages, which are themselves less specialized and less declarative than relational languages.
If you can represent your problem as a map/filter/aggregate operation, it (a) becomes more testable, (b) more parallelizable in both large and small and (c) more composable. I happen to think it ends up being easier to read, write and reason about too. So I try hard to make my problems look like this, because I know the tools in that area are very strong.
Objects being more than structs with immutable data is exactly what's wrong with them. OO encourages you to reason about behaviour in terms of invariants: what's true before vs what's true after, or what is always true for an instance. I think that's strictly inferior to reasoning by transformation: what is the input vs what is the output. In particular, I think functional transformation is more composable than invariants. I'd feel happier about OO and its implicit invariants if they were made more explicit, and more than just like Eiffel: it needs to be in the type system, if possible.
> CRUD apps are mostly about synchronizing facts about the world with the world itself.
Yes, but as already stated, the world is bigger than CRUD apps and a big collection of facts about the world isn't very useful without any behavior attached to those facts. Once behavior is involved, OO becomes useful.
> but if you're trying to perform a map / filter / aggregate operation across several different collections (sometimes these kinds of things need to be done!), OO doesn't give you many tools
As those things are completely orthogonal to OO, so what? You can perfectly well use the database to do the map/filter/querying and still have an OO program; OO has nothing to do with map/filter/querying, so as I said already, this is a strawman.
> Objects being more than structs with immutable data is exactly what's wrong with them.
No, it's exactly what's right with them. It is the very reason they exist, it's entirely the point of OO to hide data and expose behavior instead through a message based interface. Structs aren't objects, so claiming objects being more than structs is what's wrong them is to claim that objects shouldn't exist and that claim is unsupportable.
> I think that's strictly inferior to reasoning by transformation: what is the input vs what is the output. In particular, I think functional transformation is more composable than invariants.
Functional programming is great, and mixes quite well with OO, but it's not what's being discussed, we're comparing OO to procedural programming as implemented by topmind and his table oriented programming here.
Both of your replies are strawmen, you cast objects as data and then complain that they're bad at being data: but objects aren't about data, they're about pluggable behavior as I've already said. You have to step outside the "data is everything" mindset to actually see what OO is and where its value lies.
Frankly you don't give me the impression than you actually understand object oriented programming, like topmind himself, you seem to be full of misconceptions related to an inability to step outside the "it's all data" mindset. I could be wrong, but that's the impression you're leaving.
Take a basic example from a simple CRUD app; say I'm selling a CRUD app to many businesses, many of which already have corporate websites with global login systems and they want my app to use their member database and login system. The solution is simple and obvious, make my authentication system pluggable and for each big client that's worth the effort, implement a custom login provider that ties my authentication into their existing systems API. Can you describe a nice scalable procedural solution to this problem that's better than the obvious OO solution?
I think you're more interested in interfaces than objects, actually. All the problems of objects - inheritance most especially, but mutable state too - aren't relevant if you're primarily interested in "pluggable behaviour", which really is an interface-oriented mindset, not an OO one.
You can certainly go very far with interfaces, as both COM and HTTP have shown. Neither are object oriented, but both abstract away implementation details to compose behaviour. How HTTP has evolved and succeeded compared to COM shows the relative strengths and weaknesses: such pluggable behaviour works best when composing larger systems, rather than in the small, at the level of implementation details. That's exactly where OO is poor.
I will still contend that a cyclic graph of mutable objects (whether they look like message-passing interfaces or not) makes for a very marshy foundation. The reason is mutability and lack of idempotence. These are critical to why functional is a better approach compared to OO. To the degree that interfaces expose mutability, and they are composed in large enough mass, the same problems as OO will arise. There's a reason I keep harping on about mutability despite accusations of straw men: it is the fundamental flaw at the heart of OO.
> Functional programming is great, and mixes quite well with OO
It does not. You have to work very hard in most OO languages to get FP right; in statically typed languages, the type system isn't expressive enough, and most dynamically typed languages default to too much mutability.
There was a chap back in the day who advocated object-oriented assembly, Randall Hyde. The discipline required reminds me of attempts to do FP in an OO language. The minimum needed for FP in OO is a decent lambda syntax (do you remember the horrible gymnastics with overloaded operators in C++ before lambdas? - boost::lambda etc); without that, it's not worth the syntactic overhead. With it, something can be accomplished. But nominal typing really sucks, and OO programmers love giving their types distinct names, explicitly preventing the reuse they allegedly are after.
From several decades of observation, I have a theory that OOP programmers get caught up in a reward feedback loop around ceremony and prefabricated patterns, a quasi-religious endorphin rush from following formalisms that aren't strictly necessary to address the problem head-on. When a programmer isn't particularly good at crafting good solutions but can follow the OOP idioms, they can create a lot of stuff fairly quickly that looks like work, and if it fits together nicely it seems like it's elegant. But it's like carpentry focused on the joins instead of the structure: at risk of becoming baroque and unstable.
Good developers can write good OO code, of course - good developers can write good code in any language. But I don't know of anything else with the same power to clarify thinking than functional composition - it levers the mind with a solid tool rather than a wobbly, lashed-together one. And objects compose to make wholes that are less stable than their parts - because they are mutable, and state spaces multiply. Mutability is always the problem in the end.
I'm going to start with a few definitions because I can see we're not using the same ones.
Functional programming: programming in a style where functions are the basic building blocks of the program and are passed around as data to other functions along with raw data, i.e. Lisp, Scheme, Haskell. (note, functional languages are not necessarily immutable).
Pure functional programming: programming in a functional style as above, with the additional insistence on not allowing mutable state at all. i.e. (no language is truly pure because side effects are a must, but Haskell comes close).
Object oriented Programming: programming in a style where objects are the basic building blocks of the program with 3 basic features, late binding, encapsulation, and polymorphism allowing the hiding of raw data behind a message passing interface so that the program is centered around object behavior rather than object data. (note neither classes nor inheritance are necessary features of OO systems, they are simply common features).
> I think you're more interested in interfaces than objects, actually. All the problems of objects - inheritance most especially, but mutable state too - aren't relevant if you're primarily interested in "pluggable behaviour", which really is an interface-oriented mindset, not an OO one.
That's a bit of a misunderstanding of both interfaces and OO. Firstly, interfaces simply define what an object looks like, it's an implementation detail and has nothing to do with OO in general. The same applies to inheritance. Inheritance is simply one means of code sharing and is not a necessary feature of an OO system, neither Self nor Javascript for example, have inheritance in their object systems. One can also build a system that is entirely object oriented without once using an inheritance hierarchy once. Code can be shared via delegation rather than inheritance.
> You can certainly go very far with interfaces, as both COM and HTTP have shown. Neither are object oriented
Again, I think you're incorrect, COM stands for Component Object Model, and most certainly is object oriented. An HTTP endpoint that supports GET/SET/PUT/DELETE is merely an object that has 4 standardized methods, it supports late binding, implementation hiding, and polymorphism, the primary features desired from any object system. Each endpoint is an object that accepts 4 messages. It doesn't have to be an instance of a class to be an object.
> I will still contend that a cyclic graph of mutable objects (whether they look like message-passing interfaces or not) makes for a very marshy foundation. The reason is mutability and lack of idempotence.
Neither of those have anything to do with OO; you can have an OO system of entirely immutable objects. Again, you're critiquing implementation details of systems you've seen that use objects and then blame objects for features those systems have that have nothing to with with OO. Just because an OO system does something bad, doesn't mean it's OO itself that is the issue.
> There's a reason I keep harping on about mutability despite accusations of straw men: it is the fundamental flaw at the heart of OO.
No, it isn't, because mutability isn't at the heart of OO. While it's perfectly valid from a functional perspective to critique mutability, it does not follow that said critique applies to OO in general. Objects do not have to be mutable, while it's common for OO systems to be mutable, what makes an OO system is not the mutability. This is again a strawman, you mean to critique mutability, not OO, they are simply orthogonal.
One of the most useful objects in systems I work on is the Money object; it's immutable, it's treated as a primitive, it's easy to reason about and won't allow things like adding different currencies together or dividing and losing pennies. Object systems contain many such objects, another one is String. Now String... well that's a very used and very useful object with a bazillions little methods on it that return new instances of String that look differently like .ToTitleCase()... again, pure OO, no mutability in sight.
> It does not.
You're mistaken, Smalltalk the language is an elegant mix of OO and functional programming. Functions are passed around to higher order functions to do work in virtually all parts of the system and nearly all standard idioms use such higher order functional programming. Here, I think you're confusing functional programming style with immutable functional programming, they are not the same thing. Lisp is a functional programming language and everything is mutable. Functional does not mean immutable.
> You have to work very hard in most OO languages to get FP right; in statically typed languages, the type system isn't expressive enough, and most dynamically typed languages default to too much mutability.
Immutability is an implementation detail of some functional programming languages, it is not a requirement. The discussion about immutability vs mutability applies to all programming paradigms and is simply orthogonal to the OO vs procedural vs functional debate.
> From several decades of observation, I have a theory that OOP programmers get caught up in a reward feedback loop around ceremony and prefabricated patterns, a quasi-religious endorphin rush from following formalisms that aren't strictly necessary to address the problem head-on.
On that we agree, but those are just bad programmers; their failures are not a critique of the OOP style itself.
> But I don't know of anything else with the same power to clarify thinking than functional composition - it levers the mind with a solid tool rather than a wobbly, lashed-together one.
I agree, but this has little to do with OO, and more to do with referential transparency. Yes, pure functions are easier to reason about than methods who depend on hidden internal state. But objects can be useful without any state at all. Smalltalk implements its conditional statements using polymorphic objects True and False, both subclasses of Boolean, and none of these classes have any state at all; their identity alone is all that is needed to implement the entirety of boolean logic purely in the library, i.e. Smalltalk has no procedural constructs like an if statement or a while statement or a for or foreach statement; it's all done with functional programming and objects.
> And objects compose to make wholes that are less stable than their parts - because they are mutable, and state spaces multiply. Mutability is always the problem in the end.
Again, since one can build a perfectly OO system out of entirely immutable objects, then obviously this critique again misses the mark and blames OO for something that simply isn't an OO thing. Yes, most OO systems have mutable state, that's an implementation choice those programmers made, they didn't have to, but they chose mutability. Your critique is against mutability, not against OO.
Hey man, accidentally downvoted this comment but I agree heartily. In fact, if you're interested in the approach you describe at the end of your comments, you should read about "event sourcing", CQRS, Redux, Apache Samza, Elm, and re-frame.
That something can be described in a procedural manner does not imply we humans work that way, nor does it preclude the fact the those same things can be described in other manners as well.
When you interact with other people, you do it the OO way, you send them a message and they either act on it or not depending on their own internal state with different people able to respond differently to the same set of messages; I can just as easily claim we humans work in OO ways.
Beyond that, it's easier to describe a sequence of steps in OO where the implementation details can be hidden and swapped out with different implementations than in procedures where the logic and implementation are all mixed together and tightly coupled.
What you're used to seeing might be better described as "class-oriented programming", i.e. Java's take on OOP. Once you realize that `this` is just an implicit parameter and objects are just closures (look at Javascript's OOP for a good illustration of this principle), and you start to think about how objects can interact reliably across interfaces/languages/networks/failures, you more-or-less inevitably end up at "message-oriented programming", i.e. Smalltalk's take on OOP, wrapped up in declarative/functional APIs.
Disclaimer: these ideas are all unoriginal and some of them directly borrowed from other commenters in this thread.
Not really, they're more functional, they have textual input and output, and can be chained together, but they don't really trade meaningful messages back and forth. Processes can be thought of as objects, and signals as messages, but the set is not extensible while OO allows the developer to define the messages.
What definition are you used to seeing? I'm a Smalltalk'er, OO has been since its creation, about objects communicating via messages while hiding their implementation details so different implementations can be swapped in, i.e. late binding, polymorphism, and encapsulation, that's rather the entire point of OO.
C++ was C with objects bolted on badly, it'd be hard to point to a worse Object Oriented language. Smalltalk is what OO was intended to be, everything else is a bad copy.
I can't believe nobody has invoked Fred Brooks yet, so I'll do it:
"Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowcharts; they'll be obvious."
Tables are a fine way to explore the testable state space of moderately complex functions where there's natural or explicit discrete points in each dimension / argument. It's easy to see which combination is or isn't tested when it's in a tabular form, vs almost any other approach.
For actually driving logic, it tends to be somewhat user unfriendly unless there's something intrinsically table-oriented in the domain. The best table-oriented program I ever worked with was a disassembler; naturally, machine code (particularly the x86 series) lends itself to tables since unused / reserved opcodes in one generation turn into prefixes in the next.
Tables do make explicit the entire state space, though, and if followed to the logical cross-product extreme, are agnostic to the hierarchy imposed by OOP. But the geometric space explosion of cross-products makes you want to create new abstractions to cover more ground, and you start to lose what tables gave you to begin with.
> But the geometric space explosion of cross-products makes you want to create new abstractions to cover more ground, and you start to lose what tables gave you to begin with.
What kind of abstractions are you thinking of? ORM?
There is an interesting comparison between tables and s-expression on c2.com that might interest you:
"The average for TOP is higher than es-exp's in my opinion, so they are the better general-purpose tool, especially if complexity of a project grows over time. I rarely see project complexity shrink over time, thus it seems more logical to emphasize the higher end when making a judgment rather than the lower end. If you see dark clouds on the horizon, you should pick the bigger umbrella even though the current rain may be light. -- top "
I'm thinking of functions with many arguments: every argument adds an extra dimension to the table. Even if you unwrap the dimensions (e.g. instead of a cube made of 1/2, a/b and x/y, you have a table of a1/b1/a2/b2 vs x/y) you still multiply the state space.
S-exprs are simply a representation for trees, and you can use trees for anything - not necessarily the most efficient way, but trees are very flexible. But trees encode policy: the choice of root and branch determines what's cheap and what's expensive. Tables are agnostic, but they pay for their agnosticism with their dimensionality.
The RS/1 statistical system used a table-based approach; it was wonderful! If I ever spend time to design a language, it's going to have "table" as the main non-primitive type, with some "hints" to sallow super speed.
Cool features: tables could be much larger than memory; they were swapped out as needed. Temporary tables would just stay in memory until memory pressure grew too high; then they were automatically spilled to disk.
What in unix are weird-looking files (termcap, I'm looking at you!) were just tables of data.
Unlike SQL table, RS/1 simply let you change tables using a nice editor (or via SQL-like commands).
Also unlike SQL tables, RS/1 tables should be grouped into objects; an XY graph was simply a collection of tables (a data table, a table for options like line color, some ticks tables, and more). Normally you edit a graph using a nice editor, but you could also edit the (documented) underlying tables.
Overall, it was the single most productive programming and data manipulation environment (for it's time) that I've used.
One of the rogue work projects I took on once that I'm still proud of was baking Excel tables into a memory-mapped block in a game engine, and having a very lightweight API for reading the table.
That makes it super easy to author state machines using Excel with arbitrary data per state. Super useful and provided a great way for designers to do the game logic in Excel while I focused on the physics or whatever else.
It was also orders of magnitude faster than any other format the studio was using for data - XML was truly disasterously awful, but even decent formats would be doing parsing, translating, copying and memory allocation per node from whatever file format they were using. Compare that to a table that is ready to go in memory after a single fread().
I still wouldn't call it Table Oriented Programming, nor suggested that it is somehow opposed to Object Oriented Programming, but I do have experience to back up how some of the ideas in this article are good.
Can you provide more details on how do you baking Excel tables into a memory-mapped block? did you used memory mapped file as in linux? or you manually synchronize between excel and a memory block in game engine?
Is there a modern language built on these (or similar) principles? Just curious. I imagine a DSL would be adequate, but it seems we still use SQL and OOP most often, with the exception of array languages. (These days even my array programming is in an OOP language, Python)
Not to say that this is necessarily a better idea, but I find it interesting enough to wonder what analogous things are out there right now.
Aside: I find this a neat example of some hidden knowledge buried in the historical web that could easily have been lost if it weren't for brave internet archivists. Kudos to the internet archive et al!
The article claims that the following languages are table oriented in some respect:
"TOP languages do exist in various levels or incarnations of Table-orientedness. These include Xbase derivatives (dBASE, FoxPro, Clipper), PAL (Paradox), Power-Builder, Perl (for certain list types), Progress, Oracle's PL/SQL, and Clarion Developer."
A quick web search shows that all of these are apparently still actively developed and used. I don't think any one is modern in the particular sense I understand your question since, apart from Perl, they were all attempts to create the fourth generation languages that were supposed to take over from structured programming.
The one place this style has been handy (for me) was managing gui actions. Imagine some code to save a file. That code should be activated on a few different events, CTRL-S, File -> Save, perhaps a save icon on a toolbar. You want a 4 way associative set. A key event comes in, lookup the action for that event in the table, SELECT action FROM bindings WHERE key=CTRL_S; (or whatever). I didn't really use a database, but it gets the concept across.
This style makes it really easy to dynamically add buttons for actions, verify you've got shortcut keys for different actions, things are hooked up in menus, things like that.
IMHO, it's of limited value. On the other hand, any time you've got several maps that all need to stay in sync something more table like is really nice. A map and it's inverse, several maps all pointing at the same values, things like that.
Efficient syntax for set operations and enabling and exposing relational algebra as simple constructs usually simplifies code (I do lot of data transforms at work). It does not require fancy constructs, just starting from the fact that a large category of programs can be expressed succintly with relational algebra and then implementing the clearest and most succint code to implement it.
Or even APL, where everything is an array (with an arbitrary number of dimensions). For example, adding two arrays together will add the corresponding elements.
Indeed, you can use Lua tables within an object-oriented paradigm, or you can use them for other things as well. The power is in metatables, which allow you to alter the behaviours of table operations according to your needs. So in that sense, Lua allows table-oriented programming indeed.
Table-oriented programming needs more tables and less code. Think spreadsheets, and systems with functional boxes interconnected by lines. Examples are LabView and Blender game programming.
Incidentally, the box with all the knobs is an art piece by Miller Levy, not a functional device. Here's the original.[1]
We need spreadsheet oriented programming. A lot of people use Excel for all kinds of programming but it gets pretty complicated. Even something as simple as being able to name a column or a cell could go a long way towards improving these Excel programs.
Improv was an attempt to redefine the way a spreadsheet program should work, to make it easier to build new spreadsheets and to modify existing ones. Conventional spreadsheets used on-screen cells to store all data, formulas, and notes. Improv separated these concepts and used the cells only for input and output data. Formulas, macros and other objects existed outside the cells, to simplify editing and reduce errors. Improv used named ranges for all formulas, as opposed to cell addresses.
The biggest problem with spreadsheet-oriented programming is that we haven't yet found the way to abstract table sections into reusable 'functions', which currently leads to tons of copy/paste.
There was a proposal to add user-defined table-based functions to Excel, by a group of authors including none other than Simon Peyton Jones, a major contributor to Haskell and GHC. Here's that paper: http://research.microsoft.com/en-us/um/people/simonpj/Papers...
Curiously, the paper is titled 'Improving the world's most popular functional language: user-defined functions in Excel'.
(And by the way, you can already name and refer to any range of cells in Excel, not just a row or a column. There's also a GUI dialog that lets you manage the names, e.g. rename, delete etc.)
I wonder if functions, or their equivalent, is what makes programming complicated for many. Because now you have one set of code that crops up in multiple places, and have different states depending on how it gets used in those locations.
The reason people reach for spreadsheets seems to be because they are visual. much like the register panel "blinkenlights" on early computers, spreadsheets allows someone to glance across and check the state of things.
And in an earlier discussion about this, someone pointed me to Apple's Numbers spreadsheet. There you can have multiple freely moveable and expandable sheets on screen at the same time, each addressable via its own name.
Many people, including Microsoft, have tried and failed extrapolate on spreadsheeting towards a more general purpose programming environment, and it turns out to be very hard. A 2D grid of editable cells without a type system makes for a great scratchpad environment, but those features are also a barrier for abstraction.
The reason this keeps failing is that people don't want to do programming in Excel. They definitely don't want to do general purpose programming in Excel. They just want to use some formulas to calculate some things.
Of course, what they're doing is programming, but the genius of spreadsheets is that it lets "non-programmers" program while hiding from them the fact that they're programming.
Agreed - I'm a heavy Excel user and I use tables for 90% of data. Tables enforce unique column names, what is missing is the same for unique row names or ids plus an easier way to relate rows, without having to use index match everywhere, which soon becomes a real pain in the arse.
A separate me product - power pivot does something like this - ms needs to add this as native functionality to Excel, since normal pivot tables are so unflexible, I never use them anymore.
One of the projects that I worked on failed miserably because the leads drank so much OOP kool-aide that they just couldn't conceive of table-oriented techniques when needed.
Updated Requirements dictate that we need to add a field? Just spend a few days adding the same field to 20 different classes and converters!
UI team asks that our REST APIs allow them to filter fields? One of the lead's head exploded because he couldn't conceive how to do that without strongly-typed objects.
I'm wondering why there's no table-based computer language, is there? We have string-based, functional, list-based, object-based. Why not one where conditionals, even arithmetic is a table lookup? Could execute terrifically fast if tables were small enough to fit into memory.