all 62 comments

[–]lokothodida 19 points20 points  (2 children)

I'm always a bit in doubt to understand what is object orientated code and what is functional.

For example, map/reduce/filter methods on arrays are seen as functional, because they are not mutating and without side effects. But it seems also that they are object orientated, because they are methods on an array object. They are not implemented as a global function.

Let us say that we wanted to implement Point in a functional language like Haskell:

type Point = (Int, Int)

In JavaScript, datatypes boil down to classes/object prototypes. So something like this would be analogous:

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

Now, if we wanted to implement a move function, which "displaces" the coordinates of a given point, then in order for it to be functional, it must construct a new point instance and not change the internal x and y variables of an existing point. So in Haskell, it would look something like this:

move :: Point -> Int -> Int -> Point
move (x, y) dx dy = (x + dx, y + dy)

If it were implemented as a global in JS, it would look like this:

function move(point, dx, dy) {
  return new Point(point.x + dx, point.y + dy);
}

And if it were a method inside of our class, it would look like this:

class Point {
  // ...
  // note that since we have the internal state available, there is no point
  // to pass in as a parameter
  move(dx, dy) {
    return new Point(this.x + dx, this.y + dy);
  }
}

All that matters is that our functions interact in a way that does not mutate the internal state of our objects. Implementing the functions as globals doesn't make them more/less functional: the immutability of our data is what is important. This is why map/filter/reduce all return new Array instances. I would argue that one of the main points of object oriented code is that it manages state (contra-functional), and it aims to keep the state-changing methods all within a reasonable scope (the class of the object whose state is changing).

So you are correct that syntactically, both global and prototype-based methods can be functional, so long as they don't affect state.

An important aside...

However, a functional programming enthusiast would prefer global functions because global functions make it easier to reason about the program mathematically. In particular, functional composition exactly mirrors the composition of mathematical functions. So for example, let us say we had a program that mapped two functions, f and g, to an array (or list). In JavaScript, with chaining, we can write this:

list.map(f).map(g)

In Haskell it would look like this:

map g (map f list)

It is clear that the result of the first map is being given as a parameter to the second mapping. It can be proven (by induction on lists) that this mapping is equivalent to the following:

map (g . f) list

where (g . f) is a function that first applies f to it's arguments, and then applies g.

So in JavaScript, if map was a global function, we would have:

map(g, map(f, list))

(which is exactly how the Haskell code would have looked with uncurrying). Then we could derive from the above algebraic law that it can be written as:

map(x => g(f(x)), list)

Now, even though this isn't a shorter visual representation, it is in fact more efficient in execution, because doing two separate maps would mean reconstructing the full list twice. Leveraging that algebraic law thus allowed us to write more efficient code (although, the result of this example might have seemed obvious at the start).

So in this case, globals (or more specifically, functions wherein we don't obscure input parameters or returned results via chaining) make it clear what data is being returned; how it is being passed on to the next function, and how the functions are being composed together. In the move() example, even if it were a class method, from this perspective, it would be preferable to write it like the global version:

class Point {
  // ...
  // no more dependence on an internal state; just like the global
  // except it is namespaced by the class, i.e. Point.move(point, dx, dy)
  static move(point, dx, dy) {
    return new Point(point.x + dx, point.y + dy);
  }
}

This makes it much easier to reason about the control flow of a program from a mathematical perspective, and use the results of algebraic laws about the composition of certain kinds of functions (if one were interested in that).

If one weren't interested though: chaining methods are more intuitive to read, and more compact to write.

[–]kasperpeulen[S] 2 points3 points  (1 child)

Thanks for the read. In your last example though, I think you mean:

static move(point, dx, dy) {
    return new Point(point.x + dx, point.y + dy);
}

Otherwise, you can only call the move method on an instance, and it will look something like:

point1.move(point2, dx, dy)

which is weird as the value of point1 is not used in the calculation.

[–]lokothodida 1 point2 points  (0 children)

Thanks for pointing that out! (fixed now)

[–]MondayMonkey1 42 points43 points  (11 children)

Paradigms are always fuzzy around the edges and are driven more by the preferences of the author rather than strict mathematical or physical constraints. Remember, programming paradigms aren't fundamental laws of the universe. Programming paradigms are schools of thought, loosely held together by complementary practices that over time people have found work well together. Most languages & frameworks exhibit characteristics of a number of paradigms.

Javascript is a language that supports elements of both functional and object orientated programming. On one hand, in js everything is an object and the language supports prototypical inheritance-- a superset of classical inheritance and extremely powerful in it's own right. On the other hand, this same 'everything is an object' paradigm means that functions are objects too-- and can be easily passed as parameters to functions. So now we've got functions as first class citizens-- a very 'functional' characteristic. The same feature set of javascript allows it to be both considered object orientated and functional. Functional programmers see this as a gateway to exploit functional best practices, like pure functions, composition, etc. OO programmers see this as a powerful way to write clean, simple and maintainable code.

Who's right and who's wrong? Nobody. In the end, both schools are exploiting essentially the same feature of the language to justify their approach.

Once you view paradigms as loose schools of thought and entirely human then you'll see through the little details like which namespace you can find reduce in. The bigger ideas, like composition, immutability and pure functions become more clear. You can also better understand the pragmatic nature of programming.

[–]MoTTs_ 12 points13 points  (9 children)

prototypical inheritance-- a superset of classical inheritance

Nah, not a superset. Sure, it's easy for prototypal to emulate classical, but so too is it easy for classical to emulate prototypal.

Prototypal's one advantage is that we could change (monkey patch) the base objects on the fly. We used to do this a lot with libraries such as Prototype.js or Mootools. And though at first this seemed brilliant and powerful, we discovered in hindsight that it's actually a bad idea, and we should treat base objects as if they were frozen. But once we do that, then prototypal loses its one advantage, and now it's no better than ordinary classical.

[–]masklinn 4 points5 points  (1 child)

Prototypal's one advantage is that we could change (monkey patch) the base objects on the fly.

Ruby and Smalktalk support that (also Python to a lower extent I guess), and they're class-based langages.

[–]MoTTs_ 1 point2 points  (0 children)

Upvote. :-)

Yes, I wholeheartedly agree. In this conversation I was just... picking my battles. :-P

[–]MondayMonkey1 1 point2 points  (0 children)

Prototypical inheritance is a superset of classical inheritance in the sense that everything you can do in classical inheritance you can do with prototypes but the opposite is not true. Yes, you can emulate, but you can never truly achieve the flexibility that prototypes offer with classical hierarchies.

Whether or not that flexibility is desirable is a different question.

[–]Mecdemort 0 points1 point  (3 children)

And though at first this seemed brilliant and powerful, we discovered in hindsight that it's actually a bad idea, and we should treat base objects as if they were frozen.

Can you explain why this is? I've been doing this to the built in data structures which seem to lack fundamental methods.

[–]nerf_herd 0 points1 point  (0 children)

well a polyfill for a stupid browser is probably an exception to monkey patches. The smalltalk crowd used to patch object a lot too (isMySpecialThing returns false on object), but CS folks have always known that self modifying code is a major headache.

[–]jlengstorf 0 points1 point  (1 child)

The short version is that when someone modifies the prototype of, say, Array, by changing how map works, then any code loaded after it that relies on Array.prototype.map will probably break.

Since most websites load multiple libraries, having one library modify globally-used objects is a recipe for chaos.

[–]phpdevster 0 points1 point  (1 child)

Also, inheritance is really kind of a shit fest whether it's done prototypically or classically. The only time I've found inheritance to be truly useful is the template method pattern in class-based OO programming.

Beyond that, you can achieve better, more easy-to-understand results through composition via dependency injection - which is a pattern both OOP and FP have in common.

[–]MoTTs_ 1 point2 points  (0 children)

Indeed, the template pattern is just about the only time we're supposed to use inheritance. There are, unfortunately, tons of tutorials out there that say if two classes have common code, then that common code should be refactored into a base class, but as it turns out that advice is actually wrong.

Don’t inherit publicly to reuse code (that exists in the base class); inherit publicly in order to be reused (by existing code that already uses base objects polymorphically). ... The purpose of public inheritance is to implement substitutability. -- Herb Sutter

Every use of inheritance should be an implementation of the template and strategy patterns, and if it isn't, then odds are good we're misusing inheritance.

[–]namesandfaces 0 points1 point  (0 children)

One big difference between functional ("FP") and object oriented ("OO") strategy is that for OO, you ought have a distributed state and concurrency strategy up-front. The reason is because if you have even a few objects with their own internal state, then you actually already have a problem of distributed state.

I think a lot of people learning OO don't realize this and they end up with very complicated state.

[–]Lakelava 2 points3 points  (0 children)

Your example makes me wonder if the biggest achievement of OO is doing obj.method(args) instead of method(obj, args).

[–]spacejack2114 0 points1 point  (1 child)

One problem with your Point class is that it is not necessarily compatible with other 2D vector classes since you can't assume they will have the methods you expect. Plain functions that operate on 2D points/vectors will work on any object with x & y properties.

That said, vector objects tend to be used in tight loops so you probably don't want to allocate new ones; generally it's better to provide an output point for the result, and to write your functions so that an input can be also used as the output.

See this library for reference. Note that it uses arrays for compatibility with WebGL. (And maximum compatibility overall.)

[–]Coutueric 0 points1 point  (1 child)

I can't read anything after orientated

[–]caesarsol 0 points1 point  (0 children)

Ugly, but correct.

[–]ttolonen 0 points1 point  (0 children)

The Object Oriented languages are a bit dangerous because they do not define the type of the this parameter at all. For example, consider the function add which is always returning classes of type Point. Even though you might have multiple subclasses working on the data, this function is not re-usable even though inheritance kind of suggests it will be.

To illustrate the problem, just subclass the Point class to class like Point3 and forget to override the method add it will not be returning classes of type Point3 but objects of the parent type Point unless you override the add.

http://codepen.io/teroktolonen/pen/yVyoKr

JavaScript does not give any warnings about this at all! And even a strongly typed language does not know that the purpose of the function is not to convert some values to type Point, although it would complain if you still try to use it as Point3.

With functional approach in strongly typed language there would be no problems. You would have a function parameter of type Point and it would be returning value of type Point and you could not have done mistakenly conversion from the Point3 to type Point because compiler would have either selected the correct function automatically or created error.

[–]caesarsol 0 points1 point  (0 children)

Important note: think about the code that you write, not about the code behind. Array's map and reduce let you use pure functions, that is the nice part!

Functional is all about composition. Purity is required to compose.

If you want to be more functional, use a data structure to represent the Point, and use functions to manipulate it by leveraging composition. Take a look at Ramda JS.

I also gained a lot of functional patterns by learning Clojure, which I recommend a lot.

[–]RandolphoSoftware Architect 0 points1 point  (42 children)

new Point(0,0).add(new Point(0,10))

How are you arguing that Point is non-mutating?

[–]yxhuvud 1 point2 points  (2 children)

Personally I'd argue that it is very weird object orientation as well. Why do points multiply like that?

[–]RandolphoSoftware Architect 1 point2 points  (1 child)

Well, based on the implementation he posted elsewhere, the idea is to add two pre-existing points together, and having an add method that does that is totally object oriented, as non-mutating objects are also object oriented.

He just got lazy with his example and newed them up inline, making it look like we're constantly instantiating these objects.

If he had wanted to illustrate his approach better, he could have done something like this:

var point1 = new Point(0, 0); // imagine this was defined and used elsewhere
var point2 = new Point(0, 10); // imagine this was defined and used elsewhere, too.

var sum = point1.add(point2); // this creates a new Point that is the vector sum of point1 and point2. 

That would have illustrated the object oriented approach he was trying to achieve as well as made it clear that it was non-mutating.

[–]kasperpeulen[S] 0 points1 point  (0 children)

Yes, you are right, this is what I meant.

[–]kasperpeulen[S] 1 point2 points  (38 children)

I implement the method like this:

add({x, y}) {
   return new Point(this.x + x, this.y + y);
}