Subject: DRY vs. KISS

From: Shlomi Fish <[email-masked]>
Date: Thu, 12 Nov 2009 19:24:19 +0200

Hi all!

First of all - DRY is http://en.wikipedia.org/wiki/Don%27t_repeat_yourself - 
the principle of software design where one assumes they should avoid duplicate 
code and repeating various stuff. Likewise - KISS is 
http://en.wikipedia.org/wiki/KISS_principle - "Keep it simple, stupid" - the 
principle of software design (or whatever) where you should try to avoid being 
too clever or doing too much.

Lately, I've often been thinking that some places where I tried to avoid 
repeating myself (and violate the DRY principle) have caused the code to 
become too clever and as result hard to get into, at least for the uninitiated 
(and thus violate the KISS principle). For example, in 
http://web-cpan.berlios.de/modules/Test-Run/ , I created a small abstraction 
to run several methods, one after the other in a sequence, after breaking 
several methods into shorter ones. So:

sub myfunc
{
	# $self is the object handle, $args are the other argument
	my ($self, $args) = @_;

	$self->one($args);
	$self->two($args);
	$self->three($args);

	# Return nothing because this is a procedure
	return;
}

Becomes something like

<<<<<<<<<<<
__PACKAGE__->add_seq_func( [ "myfunc" ] );

sub _myfunc__list
{
	return [ "one", "two", "three", ];
}

And add_seq_func will generate this method by using run-time code-generation 
(not necessarily string eval, but also possibly using closures, etc.). I gave 
the syntax in Perl out of convenience but such hackery is possibly in Ruby and 
other languages.

Now I think the second version, while being more modular, is very non-KISS, 
and will likely confuse people.

Today I ran into another example. In:

http://www.shlomifish.org/open-source/projects/XML-Grammar/Fiction/

(and XML-Grammar-Screenplay from which it was derived) I had something like 
that:

<<<<<<<<<<<<<<<<<<<

        $self->_with_curr_line(
            sub {

                my $l = shift;

		# Use a regex on the string pointed to by $l			
                
		${$l} =~ m{\G([^\<\[\]\&]*)}cgms;

What happens here is that _with_curr_line is a method that accepts a 
subroutine reference (that can be a closure), which in turn gets passed a 
reference (which is the Perl idea of a pointer) to the current line in the 
parser, which I can use while being able to modify it. Eventually I changed 
all the code there to:

<<<<<<<<<<<<<<<<<<
my $l = $self->_curr_line_ref();

# Do something with ${$l} and be happy.

Arguably, I now have duplicate "my $l = $self->_curr_line_ref();" calls all 
over the code but the code is simpler this way. I'm not an anti-closure 
purist, and think they have many legitimate uses, but felt that the old 
version was confusing, and created another pattern in the code (and possibly 
an anti-pattern) that wasn't much better than what I had.

So the question to you is: do you think DRY and KISS can sometimes contradict 
one another? If so, which one is more important, or that you should just use 
your reason. I should also note that I've been feeling that less expressive 
languages sometimes result in being unable to avoid duplicate code or 
undesirable "patterns" in the code (in accordance to DRY). Possibly the only 
family of languages that completely avoids duplicate code is Lisp.

Regards,

	Shlomi Fish

-- 
-----------------------------------------------------------------
Shlomi Fish       http://www.shlomifish.org/
Interview with Ben Collins-Sussman - http://shlom.in/sussman

Chuck Norris read the entire English Wikipedia in 24 hours. Twice.

---

From: Omer Zak <[email-masked]>
Date: Thu, 12 Nov 2009 20:41:09 +0200

On Thu, 2009-11-12 at 19:24 +0200, Shlomi Fish wrote:
[... an example was snipped ...]
> family of languages that completely avoids duplicate code is Lisp.

I think that more important than DRY, KISS or whatever is the principle
of not overloading the memory of whomever is going to read the software.

You overload his memory if he has to look up elsewhere for definitions
of variables, functions, closures or whatever constructs you are using.
According to this criteria, LISP can be very bad.

If you define another function in order to avoid code repetition at 3
places in your project, then whomever is reading the code needs to look
up the new function definition.  If it's the only function in the
statement - OK.  But if the statement already invokes 7 functions to
compute values, return a function pointer to be invoked, and the address
of the variable to receive the result, then adding 8th function would
already overflow the reader's short term memory.

And sometimes when you need to modify all copies of an expression in
your project, egrep can do good enough work - so no need to make them a
function to enable code modification at one place.  In a way it would be
"virtual DRY" - the entity which you don't repeat is the egrep pattern.

Those are vague ideas.  I hope that someone would continue the
conversation and can distill those ideas and find the hidden principle -
it surely would be a gem!
                                              --- Omer
-- 
Eli Marmor's Law:  netiquette forbids people from replying "I agree with
you" to messages in mailing lists, skewing discussions in favor of the
troublemaker type of dissenters.
My own blog is at http://www.zak.co.il/tddpirate/

My opinions, as expressed in this E-mail message, are mine alone.
They do not represent the official policy of any organization with which
I may be affiliated in any way.
WARNING TO SPAMMERS:  at http://www.zak.co.il/spamwarning.html

---

From: Shlomi Fish <[email-masked]>
Date: Fri, 13 Nov 2009 23:44:15 +0200

On Thursday 12 Nov 2009 20:41:09 Omer Zak wrote:
> > So the question to you is: do you think DRY and KISS can sometimes
> > contradict one another? If so, which one is more important, or that you
> > should just use your reason. I should also note that I've been feeling
> > that less expressive languages sometimes result in being unable to avoid
> > duplicate code or undesirable "patterns" in the code (in accordance to
> > DRY). Possibly the only family of languages that completely avoids
> > duplicate code is Lisp.
> 

Interesting. Of course, part of the reason why we have functions is so we can 
encapsulate a potentially complex function in an easy to digest thing that we 
know what it does.

Of course, this may result in a http://en.wikipedia.org/wiki/Leaky_abstraction 
which will require us to understand what the function does and how it does it 
before we can proceed, which will also overload the memory during reading the 
code.

> already overflow the reader's short term memory.

Well, in his book "Refactoring", Martin Fowler advocates very short methods of 
very few statements with simple expressions each, while extracting the more 
complex evaluations to helper methods, and many people seem to agree with him 
(including me to a large extent[Practice]). Of course, this in turn causes 
understanding the code or tracing a bug be more difficult because you have to 
delve several methods deep (which in turn causes a memory overload).

{{{{{{
[Practice] - while I agree with it, but often my code, including my open-
source code does not exhibit this. It's just that I'm too anxious to write 
functional code, that I don't make sure it's properly factored. Normally, I 
only give an external API and do not support any internal APIs, including not 
class-inheriting API, so it's not that critical.
}}}}}}

> "virtual DRY" - the entity which you don't repeat is the egrep pattern.

What if someone who is not intimately familiar with the code changes one place 
and not all other places? What if you forget to change them? That's the danger 
of duplicate code.

Some people may argue that syntactical patterns in the language are not really 
duplicate code, but Lisp purists may disagree.

Regards,

	Shlomi Fish

-- 
-----------------------------------------------------------------
Shlomi Fish       http://www.shlomifish.org/
My Aphorisms - http://www.shlomifish.org/humour.html

---

From: Shlomi Fish <[email-masked]>
Date: Sun, 20 Dec 2009 11:54:43 +0200

> According to this criteria, LISP can be very bad.

Now that I think of it, it's possible that not overloading memory is one 
aspect of KISS ("Keep is Simple and Stupid"). But I'm not sure of that.

Regards,

	Shlomi Fish

-- 
-----------------------------------------------------------------
Shlomi Fish       http://www.shlomifish.org/
What does "Zionism" mean? - http://shlom.in/def-zionism

Bzr is slower than Subversion in combination with Sourceforge. 
( By: http://dazjorz.com/ )