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

> To me OOP is about using "templates" to create "objects", which map nicely to real world entities/concepts.

That's a really commonly held view, and I think it goes off the rails in several fundamental ways:

1. The templates as a separate language feature are unnecessary, a vestigial inheritance from previous languages. One of the things I really like about JavaScript is that it took Self's object system, so I don't need to have a class to get an object. Sometimes it's useful to have a function to generate a family of similar objects. Sometimes it's not.

2. Objects (values in the language) mapping to real world entities/concepts is a dead end. This was actually realized by the database community by 1970, with the relational model. In a relational database, you define relations which may be aspects of an entity, but except in simple cases the entity exists only as a smeared out set of aspects across tables. And further, there need not be an entity that all those aspects describe, or there may be a complex web of entities and different sets of aspects may pick out contradictory, overlapping members of that set. I have found the terminology of phenomenology (suggested reading: Sokolowski, 'Introduction to Phenomenology') as a really good vocabulary for thinking about this.

3. The original motivation for OOP was very different. Consider that you're exploring an idea for a new programming paradigm, approach to AI, something that expresses itself in a mathematical formalism. One of the bottlenecks of CS and AI research was implementing these formalisms as programming languages. OOP came from the observation that you can express these formalisms in terms of message passing actors, and message passing actors are a straightforward thing to implement. So if you have a programming system that's built on message passing actors and you want to try a new formalism, you can add a new arrangement of message passing actors to the same, live system to implement your formalism, which is a lot faster than writing a whole new compiler or interpreter.

> Can someone describe FP in a similar manner to someone like me?

The impetus for FP comes from Backus's Turing lecture "Can programming be liberated from the von Neumann style?" Backus's observation was that thinking programming in terms of "do A, then do B, then do C" doesn't scale to intellectual complexity very well, and pointed out that the obvious way to scale is to have some kind of "combining forms" that let you combine hunks of programs, and that a somewhat restricted form of function is particularly easy to combine.

The simplest version that we all know is a shell pipeline. This assumes that each command is a function from string to string and uses a function | to compose functions. We take it for granted that each shell command can't mess with or look at the inner workings of the other ones in the pipeline (a restricted form of function that is easy to combine).

In bash you would write

    ls . | grep pig | wc -l
In Haskell you would write

   (getDirectoryContents .) . (filter (=="pig")) . lines . length
The next step is map, filter, and fold. It turns out that fold is as powerful as loops, so those three together are a complete set of programming primitives, and they're super easy to combine.

And then you start climbing up the ladder of abstraction, looking for similarly easy sets of primitives that you can express stuff in and combine them. Monads, applicative functors, arrows, lenses...these are all structures that people have found while building such sets of primitives. There's nothing magical about them from that point of view. It's kind of like the fact that sequences (lists, arrays) show up everywhere in procedural programming.



I appreciate your answer.

programming in terms of "do A, then do B, then do C" doesn't scale to intellectual complexity very well

I think that's the bit I'm struggling to understand. You gave an example of shell pipe, but that's easy to implement with simple procedural programming:

  f = ls(.)
  g = grep(f, "pig") 
  h = wc(g, mode="lines")
  print(h)
This code is easy to understand, and the advantage of doing it in Haskell is not clear. I guess it's not "intellectually complex" enough to demonstrate the power of FP. Is there some example where I'd study it for a bit, then realize "Yes. This is nifty way to do X", where X is some task that is cumbersome (or dangerous, or ...) if done in procedural or OOP style of programming. Because I definitely had a moment like that when I was looking at OOP examples.


That's a really clear statement of what you're after, and I'll try to provide a good answer.

When you write a procedural program, there are larger patterns or strategies that you use again and again, such as the simple pipeline you show above. Or you repeat until a condition is met, or you iteratively adjust a value. Any competent procedural programmer deploys these strategies with barely a thought. They aren't things in the language that can be manipulated and combined. And, truthfully, a lot of this has been dragged into what we think of as procedural languages today.

So consider a procedural implementation of listening on a socket and on each connection reading the request and sending a response. It's straightforward to write in a procedural language, but if we want to reuse that behavior, we need to parameterize it over the handler. That may seem obvious today, and it's a trick that C programmers have known forever (see qsort in the stdlib). But imagine setting up the whole language like that, making all the strategies that we toss out so blithely in procedural code, into higher order functions.

This pays off because you can debug and perfect the strategy and the particular parameters for behavior separately, which turns out to be a really useful separation. Get the socket listener code working where the response is echo, then plug in your web app's handler.




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

Search: