7

In the blog post Don't structure data all the way down, the author discusses different ways to structure a circle datatype and how to implement the parameters to a Radius function.

area({circle, {point,0,0}, 17.4})

Kind of messy. What about a function to build a circle for us? Then we could do this:

area(make_circle(0, 0, 17.4))

We could also have a shorter version of make_circle that only takes a radius, defaulting the center point to 0,0. Okay, stop, we're engineering ourselves to death. All we need is a simple function to compute the area of a circle:

area(R) -> math:pi() * R * R.

He is talking about the functions in the Erlang language, which is functional and not object oriented (OO). My question is, does this argument hold for OO languages, and does it hold for functional languages?

6 Answers 6

5

I guess the gist of it is "don't over-engineer". The OOP equivalent of his argument would use a class Circle, a bit like this one (canonical yet hypothetical class-based OOP language assumed):

public class Circle {
    private Point center;
    private double radius;
    public Circle(Point, radius) { ... }
    public Circle(radius) { ... }
    public double getArea() { return radius * radius * Math.PI; }
}

...when in fact, all you need is:

public double getCircleArea(double radius) { return radius * radius * Math.PI; }

Of course, "over-engineering is bad" is an easy statement to make, and under-engineering is equally bad. The trick is to find exactly the right level of abstraction, and learning this takes experience.

Object-oriented programming is especially vulnerable to the over-engineering trap though, because it offers so many neat ways of adding layers of abstraction; most of the time, adding yet another abstraction on top of your stack feels like the right thing to do, even though too often it's not.

4

The point is "You ain't gonna need it" (aka YAGNI) and therefore you should not implement it. This has nothing to do with the programming paradigm (OO or functional).

2

I think the answer depends entirely on context. It nearly always makes sense to build abstractions for anything which potentially recurrs all over the place in an application, even if that abstraction is only lightweight it's more likely to provide cleaner code further up

For example, your logic layer might need to build a more complicated algorithm which determines whether two circles are touching or intersecting, and determine the points at which they touch; this kind of code would benefit IMO with the ability to condense two sets of X,Y,Radius into a single structure. - The user of the algorithm is probably dealing specifically with circles in this case, and the circle itself may end up with a larger interface than simply Get/Set.

At the other end, sometimes a structure is simply overkill if the abstraction ends up being a one-off use "throwaway" - if the circle has no other potential use except for a single localised scenario calculating the area, then a very simple area calculation would do just fine

1

Using an OO language or OO style, you have a hierarchy of structure elements. For example (intentionally very simplified):

  1. statements, variables
  2. functions / member functions
  3. classes/objects
  4. libraries/components -

So you typically build more or less complicated classes from simpler building blocks like functions and variables. And it seems to me that's all what the blog post is all about: keep the lower level building blocks (like a function for calculating the circle area) simple, and the data structure "one layer up", which could be a "class" in an OO language. This may pretty obvious to someone who is used to design data structures in an OO manner, but may be not so obvious when using a pure functional style.

In fact, that does not mean you should ever implement something like an area function outside the scope of a circle class - you just should not give it parameters it won't need anyway (like the X/Y coordinates).

0

I think this can be summed up as don't create an object only to call a single method on it and then destroy it and this is sound advice in the OOP world. I remember seeing a pycon video on it infact.

In the functional world, especially in Haskell (as its lazy), the opposite is often true. A common pattern is to build up a huge data structure, only to immediately break it down. Being lazy and if done right, Haskell doesn't use much memory doing this.

0

The point I see in that circle area example is that your functions should not require parameters that they don't use.

A circle area function only needs to know the radius, so it should only take the radius as a parameter. This principle can be extended from simple functions to any size chunk of code. Your classes should not depend on other code they do not need. Likewise your dlls should only require other dlls that they actually need.

Minimizing the dependencies and data your code requires makes it inherently easier to use and easier to reuse. It also makes unit-testing much easier.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.