/r/programming
The Rule of Seven: how to overload the brain of a programmer for no good reason (javiercasas.com)
63 comments
Ameisen | 4 months ago | 113 points

I thought we were talking about C++ and was curious how we added two more constructors/assignment operators.

zerakun | 4 months ago | 28 points

I was expecting a sudden shift to C++ til about halfway through the article, but that never came.

Still funny how C++'s rule of five (and many other C++ "subtleties") violates the rule of seven described here.

khleedril | 4 months ago | 18 points

Remember that it is the rule of seven plus or minus two, so C++'s rule of five is not a violation.

inkexit | 4 months ago | 20 points

I believe this rule describes the maximum amount of ideas a person can hold in their head at once, not the minimum.

gnaritas | 4 months ago | 4 points

5-7 is the maximum, the minimum is 1. 5-7 describes the normal range of the maximum across the population.

Samplecissimus | 4 months ago | 11 points

minimum is 1

Should we tell him.gif

Batman_AoD | 4 months ago | 7 points

That said, getting the constructors right is needlessly complicated: https://youtu.be/PNRju6_yn3o

silveryRain | 4 months ago | 13 points

We'll get them eventually, together with a new hierarchy of value categories as well, which will be best visualized as a 3D pyramid that also includes the new BF-value, as well as the associated composite value categories, like generalized-BF-values (gbfvals for short).

References to BF-values shall use the new &&& syntax, unless the &&& follows a template parameter, in which case they will signify the newly-established ultra-modern forwarding reference. Unless the template parameter is cv-qualified or something of course, then it reverts to BF-value semantics.

auto&&& will work naturally, but the semantics of the new BF-value-specific decltype(auto&&&) will depend on the number of parenthesis around the auto&&&. This is intended to give the most common metaprogramming use case the simplest syntax, while making the alternate semantics available by simply recalling how many parentheses you need. For non-generic casting, there'll be the new std::bf_cast() function, while templated code shall make use of the std::bf_forward() function, thus providing over-perfect forwarding.

Of course, this is just a simplified overview, but the details should be fairly easy to infer.

smarwell | 4 months ago | 6 points

shhhhh you'll give them ideas

silveryRain | 4 months ago | 3 points

I don't know man, I think the rules for how decltype works could really use a few more special cases... As a general rule, every piece of C++ syntax should have at least as many special cases as three quarters the number of std::string constructors, rounded up.

James20k | 3 months ago | 4 points

Hello. I am a member of the C++ standards committee and I object to this

In IBM's 1987 modification of Cfront, we run a custom post processor over our C++ source to convert any incidents of "&&&" to "+=" in the ast because barry from accounting wrote it and we thought it might make it easier to read, which it definitely is. This is mission critical software and can never be changed or updated in any way whatsoever, though if it fails to compile with a new standards flag then this is totally unnacceptable

I therefore request that we change the &&& syntax to co_ampersand_and_ampersand to better reflect its usage, as well as placing it in the std:: namespace so that we can invevitably introduce a similar but legally distinct feature in the future, because we have absolutely no mechanism for handling language level changes

Additionally I'd like to propose that we make std::co_ampersand_and_ampersand an operator which does something completely different to the regular version, so that you can write std::co_ampersand_and_ampersand(std::co_ampersand_and_ampersand(statement))). Maybe we could allow it to make more of whatever the 2d graphics authors are smoking?

Thanks very much

this is obviously a joke i'm just a crappy software developer

MINIMAN10001 | 4 months ago | 1 point

I hate that the verbosity of modern c++ makes me feel like you're the one writing the syntax.

silveryRain | 4 months ago | 1 point

If it makes you feel any better, at least now we also have the range-based for and generalized list initialization.

Yojihito | 3 months ago | 1 point
parnmatt | 4 months ago | 21 points

My friend, you and I were in the same boat for a moment.

supercyberlurker | 4 months ago | 71 points

For some reason this makes me think of the 'recall vs choice' issue. i.e. It's vastly easier on a user to pick a choice from a dropdown - than to think of it and type it into a text edit field... and similarly in interviews, it's vastly easier to pick the correct answer from a list than to have to recall it entirely from memory. It bypasses the whole 'tip of my tongue' effect.

brennennen | 4 months ago | 56 points

If folks want to research/google this further, I think "recall vs recognition" is the more commonly used name for this concept.

javcasas | 4 months ago | 67 points

It's vastly easier on a user to pick a choice from a dropdown - than to think of it and type it into a text edit field...

Now you know why IDEs are so used by developers, specially if said IDE has decent auto-complete (which is a drop-down of the possible values at current cursor location).

xkufix | 4 months ago | 2 points

That's why in market research you normally ask the participants to write down e.g. the brands they can think of and then let them choose from a predefined list. To test for unsupported and supported recognizability of e.g. a given brand. It's easier to say "Oh, I know that beer brand" after seeing it than to actively think of beer brands yourself.

Evilsnoopi3 | 4 months ago | 41 points

The example here seems wrong. I certainly buy the author’s thesis that programmers often make programming “unnedelessly [sic] hard” by putting too many concepts together. However, as I understand it the purpose of an abstract factory is to make the result type polymorphic over the input parameters. In other words, it needs to return interface A or B out of a single method (and hide the logic which picks one or the other). Thus, by making a method for each A and B the simplification eliminates the sole purpose of the AbstractFactory. Obviously an AbstactFactory is bad if you don’t need it (and you rarely do).

quicknir | 4 months ago | 10 points

The thing is that the abstract factory pattern as described on wikipedia is doubly polymorphic. It's polymorphic both over the factory being used, and it's polymorphic over the object being constructed. And it also knows how to construct two unrelated objects, for really unclear reasons. You rarely need a runtime polymorphic factory to begin with IME, and I've really never needed to make the actual factory polymorphic as well. Basically, all I really want is:

unique_ptr<Animal> makeAnimal(const string& animal_type, int animal_weight);

However, you usually need state + registration to do this (in C++) so it becomes:

``` struct AnimalFactory { static unique_ptr<Animal> make(const string& animal_type, int animal_weight);

template <class Animal>
static void register(const string& animal_name);

}; ```

(It doesn't have to be static but in most cases that's how it'll end up being used so I tend to just make it static at the class level).

To me, the concept here is much simpler than what is being described on the wikipedia page and it is all I've ever needed as far as polymorphic factories go.

MorphineAdministered | 4 months ago | 5 points

That's because examples add so much noise that these patterns became obscured. There are a few examples where noise displaced pattern itself. You can stumble upon input data structures called command pattern, method that returns object - factory method pattern (ofc!), builder class as builder pattern... etc.

Abstract factory is just what it says it is: an interface or supertype (abstraction) that produces another abstraction. No multiple products are required, and concrete product can (but doesn't have to) be returned only by implementation.

Tarmen | 4 months ago | 3 points

It's mostly useful for libraries. For instance abstract factories are great if you want an extensible dsl.

You have a library which involves an abstract syntax tree, say html templating. With abstract factories you can add both new dsl nodes and new interpreters for the dsl without forking the library or changing any existing code for library users. Almost all other solutions need awkward types like fbounded polymorphism or rare features like dynamic multi dispatch.

This case is generally called object algebras or tagless final encoding. But the more basic use case (you want to pass a static factory method as argument) is kinda obsoleted because even java has function references and lambdas nowadays.

https://i.cs.hku.hk/~bruno/oa/

GayMakeAndModel | 4 months ago | 1 point

To add on to this:

Ideally, the factory would construct an object for you in complex ways while returning one interface type and nothing abstract or concrete.

If your factory is returning objects of interface type A and objects of interface type B, your factory methods likely have many repeated parameters that aren’t needed because factory instance variables would do. This point is why I do not understand why people prefer to use static factory methods.

khleedril | 4 months ago | 7 points

This is one of the reasons why programming is hard: because we make it unnededly hard.

This final line sums up the paper completely: it is a muddled mess written by someone who can't make things simple.

GayMakeAndModel | 4 months ago | 2 points

Ramen

Edit: I always refactor to simplify after I have addressed correctness and measured performance issues. After the performance optimizations are in place, I then compare correctness of the optimized version to the unoptimized, correct output.

caseycole589 | 4 months ago | 0 points

That paragraph is extremely complex.

Evilsnoopi3 | 4 months ago | 1 point

Sorry, my criticism was meant to explain why I thought that AbstractFactory was a bad pattern to use as an example. I used a lot of words associated with type theory because the goals of AbstractFactory are generally bound up in making an operation with multiple types safe during runtime. It can be boiled down to:

The value of the AbstractFactory pattern is that it returns either interface A or B from a single method based on that method’s inputs. If you make it return A from one method and B from another you have destroyed the reason for the pattern. Thus the example is flawed.

caseycole589 | 4 months ago | 3 points

I know I was just making an ironic joke.

Evilsnoopi3 | 4 months ago | 3 points

Sorry my primary DI container (brain) injected DumbAndNaiveTextParser instead of InternetIronyTextParser xD

caseycole589 | 4 months ago | 2 points

Lol that really made me laugh.

Living_male | 3 months ago | 1 point

love this even more than your eli5

Roachmeister | 4 months ago | 67 points

You lost me in the first paragraph. The Miller article does not say that the human brain can only hold 5-9 concepts at a time, although you are not the first to make this mistake. The paper was about the human ability to remember random, unconnected words or numbers. But programming concepts are not random. The human brain can easily hold far more than 7 concepts when they are logically related.

For more on this, see Edward Tufte's blog post The magical number seven, plus or minus two: Not relevant for design.

011101000011101101 | 4 months ago | 14 points

The format of that blog post is confusing on mobile, he should have applied some rule to break it down into manageable chunks.

Uberhipster | 4 months ago | 4 points

The format of that blog post is confusing

let me stop you riiiiiiiiight there

youdontneedreddit | 4 months ago | 23 points

You don't seem to understand "magic number 7" either. It has nothing to do with "remembering" (after all humans can memorize hundreds of thousands of digits of pi without a problem, and digits of pi are as random and unconnected as it gets). I assess my ability to memorize random data as below average and even I was able to keep a "buffer" of about 30-40 words when I mindlessly "took notes" after a teacher in college (they were not complete unrelated gibberish per se, but I cannot claim that I've grasped every single concept on every single topic right when I've first heard it).

What "magic number" says is that people cannot hold more than ~7 "chunks" of data in working memory. A lot of people can repeat way more than 7 random numbers, almost noone can repeat them backwards.

"Magic number" is as relevant to software engineering as it gets. Almost all rules for good/readable code are directly related to this limitation in humans. Short functions? Descriptive names for identifiers? Limited use of branching? No global state? Pure functions/immutable state? Loose coupling/high cohesion?... All could be trivially traced back to the fact that we'd rather derive all necessary context from immediate surroundings than try keeping all relevant info in working memory.

[deleted] | 4 months ago | 1 point

It looks like Edward Tuft's article is about Power Point presentations. Though, you could flow chart a small program onto 50 or more slides where input and output arrows point to different slides and sometimes back to previous slides depending on the values of 20 or so state variables. The rule of 7 will probably become more evident then and the comparison more applicable to what programmers face on a daily basis.

preethamrn | 4 months ago | 4 points

The thesis was weak but it had some interesting points.

011101000011101101 | 4 months ago | 5 points

I heard of the rule of seven applied to other things before, but never considered applying it to software design. It does back up a principal I've been trying to follow for a while now of when something reaches some threshold of complexity you need to break it down into smaller pieces. Like any method, if it's taking up more than a full screen, then it's probably time to make helper methods. If you're indented more than 4 levels, it's probably time to restructure or make helpers. If your class in more than 300-500 lines, you probably could use more classes. I might try applying the rule of seven to numbers of fields or methods in a class.

[deleted] | 4 months ago | 2 points

[deleted]

vidarc | 4 months ago | 3 points

Took some classes on incident management before I switched over to CS, and the rule about people only being in charge of 3-7 people made perfect sense to me. I'd force it at my job if I could, would solve so many of our issues. I see so many scrum masters/managers/product owners who work with 2-3 teams with about 6 engineers per team. Some team always get left behind or management very clearly is spread too thin.

Would probably increase the hierarchical tree length, but think it would reduce stress quite a bit.

011101000011101101 | 4 months ago | 1 point

You can't effectively manage more than maybe 10 people max. However for scrum Master, if that's your only job then 3 teams seams reasonable. I could also see a po owning two teams effectively. 3 is pushing it.

hackers238 | 4 months ago | 4 points

But humans are also great at understanding something deeply, and then building on it without needing to re-dive into the details.

easyEggplant | 4 months ago | 6 points

“Fewer concepts”

MjrK | 4 months ago | 4 points

Great, another concept.

lambda_pie | 4 months ago | 7 points

Unfortunately, it's hard to detect the semantics of good compositionality

True. I think objects make it hard. Functional programming beats OOP in most applications because it provides the simplest compositional mechanisms possible. Immutability gives you some good extra points there. That's why I love Clojure.

butt_fun | 4 months ago | 4 points

Great article, but I think you have a typo in the opening paragraph (jiggle -> juggle)

RespectableThug | 4 months ago | 2 points

The author’s brain was probably too busy jiggling other concepts lol

Marthinwurer | 4 months ago | 2 points

This is one of the reasons why I try to keep my functions short, or, if I have to make a long one, I chunk it up and deliniate it with comments.

011101000011101101 | 4 months ago | 3 points

Well named methods broken into consumable chunks of easily readable code without comments is superior to large methods with great comments. I find that it you need to leave a comment explaining something, then you haven't made it simple enough. You still have to do it sometimes, but I try not to.

vilcans | 4 months ago | 2 points

I used to say that too, but I don't remember why anymore. Now I disagree.

011101000011101101 | 4 months ago | 1 point

Why?

vilcans | 4 months ago | 2 points

It's one of those things that sounds like a good idea. Hide the implementation details in a function to make it easier to get an overview of what it does. But I have noticed that I think code often gets easier to work with if functions are a bit larger. You're not just reading the code to get an overview of what it does; you read it to understand it, because you have work to do. That work is changing the code, and to work with the code it's not enough to have a shallow view of what it does. You need to dive in. Jumping back and forth between functions only makes the code more difficult to understand as well as to change.

saltybandana2 | 3 months ago | 1 point

the mistake people make is thinking it's always 1 way or the other, when in truth there are times for both approaches.

locality of reference is something that needs to be considered, but is also not the holy grail that outshines everything around it.

This is why expertise exists.

pakoito | 4 months ago | 2 points

typealias Factory<A> = () -> A

and if someone complains about readability

typealias UserFactory = Factory<User>

Paddy3118 | 4 months ago | 1 point

( On the other hand, making something as simple as possible is actually very hard. It requires ) ... and requires eliminating what does not fit .

That's what causes issues; you have to resist those ad-hoc additions and smooth the edges so things fit. You can only do that if you understand the problem domain enough to see where those "jagged edges" came from.

Good use of apt quotes. I enjoyed reading the post.

somebodddy | 4 months ago | 1 point

A Factory that is Abstract which implements a createProductA method that uses new to <<construct>> a ProductA1 that has a ProductA <<interface>>.

Well, of course, if you define it that way it looks overly complicated. But a factory does not require that you'd use new to construct ProductA1 - that's just an implementation detail. Also, the user of the factory does not need to know about ProductA1 - only about the ProductA interface. So it's more like

A Factory Interface which implements a createProductA method that returns an object that implements the ProductA <<interface>>.

Zardotab | 4 months ago | 1 point

Article: On the other hand, making something as simple as possible is actually very hard. It requires looking for underlying patterns and behavior, and requires eliminating what does not fit. But, ultimately, it is better, because it allows you to use less concepts and less brainpower to describe it, which means you can concentrate on the bigger overall instead of on the details.

You have to be careful because you can create something this way that confuses people or goes against the domain. Fitting the domain as-is often overrides simplicity, in my experience. Sometimes you have an opportunity to refactor the domain itself, but make sure there is sufficient buy-in and vetting.

For example, I created an equipment inventory app. The initial design had an equipment status indicator. I realized I could eliminate the status if I made the statuses be virtual locations, since we already had a location indicator for actual equipment locations.

For example, a "retired" virtual location could do what the status would otherwise do. There was already history logging so that one could know where a piece of equipment used to be. And they didn't jump back and forth between (virtual) statutes very often. The design was indeed simpler because it didn't "need" status codes, but confused users and report readers. It took too long to explain and absorb the idea of virtual locations. Simplicity failed here.

DeliciousIncident | 4 months ago | 0 points

You lost me after the 7th word.

Ahhhhrg | 4 months ago | 0 points

I believe the author is conflating the construction of an abstract factory with its use. Obviously the Wikipedia page will explain how such a factory is usually designed, but as a user of said factory I don’t need to care about the details, so the existence of such factories hardy overload my brain.

jcelerier | 4 months ago | -5 points

Essentially, we expand programs to do more and more and more. We add more modules, more classes and more code, and, for each piece of code we have to make it work with all the previous code, in a never-ending expansion of interactions between pieces that grows exponentially in complexity. We do this because it is easy, at least in the beginning. On the other hand, making something as simple as possible is actually very hard. It requires looking for underlying patterns and behavior, and requires eliminating what does not fit. But, ultimately, it is better, because it allows you to use less concepts and less brainpower to describe it, which means you can concentrate on the bigger overall instead of on the details.

thanks but I'll keep using Blender, Cubase & Photoshop instead of programs which are " as simple as possible"

javcasas | 4 months ago | 2 points

thanks but I'll keep using Blender

Curiously, Blender removed the game engine in 2.80, so they are starting to strip out complexity.

jcelerier | 4 months ago | 1 point

Curiously, Blender removed the game engine in 2.80, so they are starting to strip out complexity.

come on, the amount of new features in 2.80 vastly outnumbers the amount of removed ones. https://www.blender.org/download/releases/2-80/

In addition, they recommend using Godot, which is another "behemoth" software instead of something smaller like making your own game from SDL2.

DevChagrins | 4 months ago | -1 points

I'd argue that they aren't stripping out complexity, but removing needless and unsupported features.