How Important Is Lean Code

Does or can YagNi apply to code and language design also?

Applied to language design, YagNi combined with OnceAndOnlyOnce mostly rejects ThereIsMoreThanOneWayToDoIt. YagNi alone (applied with ridiculous rigor) would probably result in simplistic TuringTarpit languages with minimalist communications ability (similar to UnLambdaLanguage or BrainfuckLanguage).


Under ResponsibilityDrivenDesignConflictsWithYagni, this issue came up about whether "dotted" identifiers, such as "system.print()" versus "print()" and table names in SQL column "employee.salary" versus "salary" should be included or removed.

I tend to exclude them if I don't see any immediate need based on a YagNi-like philosophy. I like my code to be relatively clean. I find it easier to read that way. PaulGraham has said similar things in defense of LISP.

Is this a personal preference, or is there a universal reason to simplify code and variable, function, method, and class specifiers?

I agree that if there is an ambiguity, it needs to be qualified. However, some people put extra specificiers for just in case there may be a future ambiguity. To me, that is almost like always calling a person by their full name just in case there is an overlap.


I think a good universal reason to keep redundant words out of code is that it reduces inertia and makes refactoring easier. If you have a choice between writing "employee.salary" or just "salary", then using the latter means one less thing needs to be changed if you ever rename "employee".

Also, whenever I see an apparently unnecessary qualifier, my first assumption is that it actually is needed, and so I'll spend time trying to figure out why the programmer wrote it that way. So, another good universal reason is that redundant words don't look redundant, and they therefore make the code less clear.

These are the same reasons I avoid things like HungarianNotation and UncleBobsNamingConventions. Unnecessary stuff in code is like having unnecessary comments. If the code is so complicated that these extra things are needed to make it understandable, then that's a CodeSmell.

-- KrisJohnson A lot of languages support a

With namespace {

    .salary = notEnuff

}

optional != unnecessary. Less code is more correct code however prefixing with the namespace adds to clarity and the IDE/compiler will pick up any mis-spellings.


Code should be as lean as possible. What isn't there can't break. On the other hand, life without namespaces is a nightmare on any large project. -- EricHodges

Do you mean without the ability of using namespaces, or the usage? I try to break large projects into many smaller projects to reduce such issues.

I mean language support for namespaces. The stuff before the dot is a namespace. I think most folks break large projects into small probjects. Namespaces reduce the hassle of sharing code. As the number of methods/fields increases so does the potential for name collision. -- EricHodges

I don't think anybody suggested removing the potential to use namespaces. The issue is whether to use them often, or just when a specific collision has been identified. I agree that if everything was qualified, the chance of future collisions would be reduced, but at the expense of longer-winded code and perhaps more effort when things are moved because there are more "paths" to be altered.

That's why we use IDEs. They type the namespaces for us and update them throughout the code when we move things. Since IDEs make using namespaces a non-issue, we use them for everything (including methods and fields).

Just because you can automate replication does not mean it is a good thing. Besides, it is still harder to read the code.

If it was harder to read the code, I wouldn't do it. I get to decide how verbose my code is. I wrote code without namespaces for many years and found the cost of resolving collisions was higher than the cost of using namespaces. Modern IDEs greatly reduce the cost of entering them.

Well, I don't. Perhaps your design techniques result in more collisions than mine.

What design techniques do you rely on to avoid name collisions?

I would have to look at specifics, I can't list them off the top of my head right now, and it often depends on the language's search/scope rules.


With regard to database schemas, if columns are not qualified, then sometimes adding new fields will "crash" existing queries because it may introduce ambiguities that did not exist before, especially in JOIN's. One solution is to make sure every new column being added to a production system is unique. (Scripts can be written to check the schemas). That may result in names like "productDescription" (or maybe "prodDescript") instead of just "description". Some find this technique overly burdensome, but the alternative of having long query code is not pleasant either. The ability to use asterisks in SELECT clauses greatly goes down, for one. IMO, it is easier to assure unique column names than to deal with huge explicit and qualified column name lists.


When you read a tutorial or language reference, do you prefer "lean" code, or code that is verbose enough that you can figure it out the first time? I like more verbose code. If maintenance is important I want the code to make sense at a glance. I want it to be as easy to absorb as a tutorial.

Often I find that the verbosity is not contributing, but merely a formality or habit.


See also: SystemSizeMetrics

I don't think SystemSizeMetrics really applies to this page. The term "lean" is a bit confusing, as many programmers would associate it with small executable size, small memory footprint, economy of instructions executed, or other forms of run-time non-bloatedness. The topic of this page seems to refer to the verboseness of identifiers and qualifiers, specifically whether or not to make explicit things that the compiler can/should resolve implicitly.

There was an issue about name collisions becoming more prevalent under "larger" systems. My approach is to make a bunch of smaller systems that communicate mostly via the database instead of having jillions of routines or classes all floating around together in your name-space. As far as topic names, you are welcome to suggest alternatives. Note that it is "lean code", and not "lean executables".


Re: My approach is to make a bunch of smaller systems that communicate mostly via the database...

That's fine if all you're doing is sharing data. Sharing code is where the namespaces become valuable. You can't call a library function via the database.

I suppose we would have to explore a specific example. But note that we can do things like:

  myParams = "(a, b, c)"
  q = query("select * from eventHandlers where....")
  while (getNextRow(q)) {
     execute(q.functionName + myParams)    
  }

"q" is an object or dictionary array and "functionName" is a column name. We could put the entire code in a table cell even. However, in my domain data, or stuff that can be factored into data, is the primary driver of system design. I tend to minimize the importance of code, at least as a large-scale design issue. Perhaps my approach can be characterized as EventDrivenProgramming, where the data and/or framework is "over" the behavior.


Just the other day I had to build a small menu system for an HTML frame (the frame was their choice, not mine). It basically had two levels: headers and links. My code basically looked something like this:

 module X {

[HTML setup code]

header("Politics")

link("United States", urlGoesHere) link("Middle East", urlGoesHere) link("World", urlGoesHere)

header("Sports")

link("Basketball", urlGoesHere) link("Baseball", urlGoesHere) link("Football", urlGoesHere) etc....

function header(title) { [format and display using HTML elements] } function link(title, url) { [format and display using HTML elements] }

} // end module

[note: the code spacing does not render properly in Mozilla/firefox]

I have seen people do things like this, but bloat it way up with dotted class "path" names, lots of instantiation of objects, etc. I make it an effort to chop or factor out the irrelevant stuff, especially for the "main" routine where most of the content and options are specified. The supporting routines create a little mini-language that fits the task and often only the task. If stuff is not relevant to the task at hand, I find a way to toss it or shrink it. Always ask yourself if your code looks like pseudo-code, and if it does not ask yourself why.

Unfortunately the language I used did not support NamedParameters?, which made variations such as custom indentation or color difficult because I had to carry a parameter for rare options. Named parameters are very useful for creating lean code in my opinion. They also make it easier to extend code because you don't have to add parameters to every call even if one is not using the new feature. For example, the first header had a slightly different spacing than the others. Thus, some way had to be found to tell that call it was the first. One can add a named parameter and use it like this:

  header("Politics", first=true)

We don't have to change the rest of the header calls because named parameters usually can take defaults ("false" in this case). However, if we used positional parameters, we would have to add the new parameter to every header call. (Some languages ignore or default missing positional parameters, but it is not good practice to keep relying on such behavior IMO. Long positional parameter lists are a potential CodeSmell.)


Another technique to simplify code is to not have a handle or object if there is only going to be one at a time. For example, if you are building up HTML text, rather than pass the string/object to every function, make it "regional" in scope (module-level). Then you can have things like:

  inputBox("First Name", "fname", 30)
  inputBox("Last Name", "lname", 30)
  inputBox("Social Sec. No.", "ssn", 11)

Instead of

  HTML = inputBox(HTML, "First Name", "fname", 30)
  HTML = inputBox(HTML, "Last Name", "lname", 30)
  HTML = inputBox(HTML, "Social Sec. No.", "ssn", 11)

Or

  HTML.addInputBox("First Name", "fname", 30)
  HTML.addInputBox("Last Name", "lname", 30)
  HTML.addInputBox("Social Sec. No.", "ssn", 11)

For small snippets it does not matter much, but it adds up over time WRT "useless eye clutter".

or you can switch to a non-brain-dead language that supports BlockClosures?:

 html{
  body{
   form(...){
    inputBox("First Name", "fname", 30)
    inputBox("Last Name", "lname", 30)
    inputBox("Social Sec. No.", "ssn", 11)
   }
  }
 }

The inputBox method is only visible inside the form{} block. Block Closures can give variables the narrowest scope possible, without extra clutter.

But often one needs fairly complex business logic inside of such areas, and the business logic may need wider scope.

   form(...){
    if (foo && bar || snoog) {
       inputBox("First Name", "fname", 30);
       inputBox("Last Name", "lname", 30)
       }
    else { 
       inputBox("Organization Name","fname",30);
       }
    inputBox("Social Sec. No.", "ssn", 11);
   }

Closures are one of those things that look cool on paper, but are difficult to translate into significant practical benefits. They're borderline MentalMasturbation in my opinion. Structured programming is one of those things that looks cool on paper, but is difficult to translate into significant practical benefits. It's borderline MentalMasturbation in my opinion.

High-level languages (like FORTRAN and COBOL) are one of those things that look cool on paper, but are difficult to translate into significant practical benefits. They're borderline MentalMasturbation in my opinion.

GOTOs and assembly language are good enough for me. Are they good enough for you?

Assebmly is more lines/volume of code per algorithm, and GOTO's are more inconsistent across programmers and lack the visual flow cues of indentation. Anyhow, rather than get into yet another general paradigm HolyWar, how about we focus on the above example. How would closures clearly make it better?

{How would they clearly make it worse? When I read your example, it is not clear that closures aren't in use. Closures don't forbid access to business logic. If you need 'foo' and 'bar' and 'snoog', those can be lexically captured when the closure is created or extant in a 'business-model' object shared by the closure and a few other objects.}

I think a potpourri of too many language features is not a good thing. It is linguistic creaping featuritice.

{Excellent job focusing on your example, top. Everybody should follow your lead and evade like hell. :-}

{I'll agree that "linguistic feature creep" and "featuritis" aren't good things in language design. To me, "too many language features" equates with "ThereIsMoreThanOneWayToDoIt" (to a limited extent - if it's just SyntacticSugar, I wouldn't count it as a different way to do it; more a problem is including a new 'feature', with specialized semantics, to do something that is already possible). In this case, the goal is to simplify the syntax (e.g. to avoid specifying the 'theHtmlObject' repeatedly) and maintain encapsulation. If you can specify what 'inputBox' means in a later part of the code, while maintaining useful encapsulation, that's a fine goal. Procedural closures are one approach (where you can describe one procedure within another). So are HigherOrderFunctions or FunctorObjects. Featuritis would be to include all three. But picking just one shouldn't be a problem.}

  // Using FunctorObject
  // (can be passed/returned with inheritance or templating.  
  //  may be painful to use (unless language supports block-local classes))
  class InputBoxFunctor {
     HtmlObject& m_html;
  public:
      inline InputBoxFunctor(HtmlObject& html) : m_html(html) {}
      inline void operator()(string caption, string var, int len) {
         m_html.addInputBox(caption,var,len); 
      }
  };
  void build_business_form(HtmlObject& theHtmlObject) {  
    ... build headers ...
    InputBoxFunctor inputBox(theHtmlObject);
    if (foo && bar || snoog) {
       inputBox("First Name", "fname", 30);
       inputBox("Last Name", "lname", 30)
    }
    else { 
       inputBox("Organization Name","fname",30);
    }
    inputBox("Social Sec. No.", "ssn", 11);
    ... add more stuff (link to privacy policy, et. al.) ...
  }
  -----------------------------
  // using procedural closures 
  // (does not imply ability to (safely) pass/return procedures)
  // (procedures might be passable if you can guarantee the stack sticks around)
  function build_business_form(theHtmlObject) {
    ... build some headers ...
    function inputBox(caption, variable, len) {
        theHtmlObject.addInputBox(caption,variable,len);
    }
    if (foo && bar || snoog) {
       inputBox("First Name", "fname", 30);
       inputBox("Last Name", "lname", 30)
    }
    else { 
       inputBox("Organization Name","fname",30);
    }
    inputBox("Social Sec. No.", "ssn", 11);
    ... add more stuff (link to privacy policy, et. al.) ...
  }
  --------------------------------
  // using higher-order functions (impure, since I don't feel like dealing with syntax for monads)
  // implies ability to pass/return the created function
  function inject_business_form(theHtmlObject)
    ... build some headers ...
    let inputBox = fun(caption,variable,len) -> inject_input_box(theHtmlObject,caption,variable,len)
    if (foo && bar || snoog) {
       inputBox("First Name", "fname", 30);
       inputBox("Last Name", "lname", 30)
    }
    else { 
       inputBox("Organization Name","fname",30);
    }
    inputBox("Social Sec. No.", "ssn", 11);
    ... add more stuff (link to privacy policy, et. al.) ...
  --------------------------------
  // simplistic macro approach (doesn't use any of the above)
  void build_business_form(theHtmlObject) {  
    ... build headers ...
    #define inputBox(caption,var,len) theHtmlObject.addInputBox(caption,var,len)
    if (foo && bar || snoog) {
       inputBox("First Name", "fname", 30);
       inputBox("Last Name", "lname", 30)
    }
    else { 
       inputBox("Organization Name","fname",30);
    }
    inputBox("Social Sec. No.", "ssn", 11);
    #undef inputBox
    ... add more stuff (link to privacy policy, et. al.) ...
  }

{All of these involve some degree of closure to syntactically capture 'theHtmlObject'. Two other alternatives are, as described above, to write 'theHtmlObject' each time (ugly) or to make 'theHtmlObject' global so 'inputBox' can also be global (bad encapsulation). However, ALL these possibilities involve a requirement for describing what 'inputBox' means in the given context, which will violate OnceAndOnlyOnce the moment you need more than one 'build_xyz_form' procedure. (You'll need to describe 'inputBox' at every call-site you ever use 'inputBox'). So, supposing you want to get rid of that problem, none of the above approaches will help. 'BlockClosure?s', though, look like they'll work, and I can think of equivalent options for the ObjectOrientedProgrammingLanguages and FunctionalProgrammingLanguages. And there is also an enhanced macro approach if you DO have functor objects, procedural closures, or higher order functions.}

  // akin to BlockClosure? feature utilized above 
  // (call this the 'procedural language' approach)
  // requires some sort of definition phase for 'form'  
  defblock form(htmlObject) 
     method inputBox(caption,var,len)
       htmlObject.addInputBox(caption,var,len);
     method radioButton(list of choices)
     method dropDown(list of options)
     method submitButton(destination) 
     method cancelButton(action) 
     method textBox(size)
     ... etc. ...
  // utilized inside other blocks
  void build_business_form(theHtmlObject) {  
    ... build headers ...
    form(theHtmlObject) {
      if (foo && bar || snoog) {
         inputBox("First Name", "fname", 30);
         inputBox("Last Name", "lname", 30)
      }
      else { 
         inputBox("Organization Name","fname",30);
      }
      inputBox("Social Sec. No.", "ssn", 11);
    }
    ... add more stuff (link to privacy policy, et. al.) ...
  }
  ----------------------
  // object-oriented equivalent to block closure feature utilized above
  function build_business_form(theHtmlObject) {
    in theHtmlObject {
       ... add some headers ...
       if(foo && bar || snoog) { 
          //<- (foo, bar, snoog not in theHtmlObject; 
          //    grabbed from lexical or dynamic scope)
          addInputBox("First Name", "fname", 30);
          addInputBox("Last Name", "lname", 30);
       }
       else {
          addInputBox("Organization Name", "fname", 30);
       }
       addInputBox("Social Sec. No.", "ssn", 11);
       ... add more stuff (link to privacy policy, et. al.) ...
    } 
  }
  ------------------------
  // functional programming equivalent to block closure (impure; could use monads for pure)
  // essentially a special form of 'let' that works for records, called 'inrecord' below.
  // could call it 'modules' or something else.
  function form(theHtmlObject) ->
     letrec inputBox     = fun (caption,var,len) -> injectInputBox(theHtmlObject,caption,var,len)
            submitButton = fun () -> ...
            cancelButton = fun () -> ...
            radioButton  = fun (list of choices) -> ...
            dropDown     = fun (list of options) -> ...
            submitButton = fun (destination) -> ...
            cancelButton = fun (action) -> ...
            textBox      = fun (size) -> ...
            etc.
      in {'inputBox'=>inputBox, 'submitButton'=>submitButton, (... big record with each component ...) }
  // (above can be in common header file)
  function inject_business_form(theHtmlObject)
    ... build some headers ...
    inrecord form(theHtmlObject)     
      if (foo && bar || snoog) {
         inputBox("First Name", "fname", 30);
         inputBox("Last Name", "lname", 30)
      }
      else { 
         inputBox("Organization Name","fname",30);
      }
      inputBox("Social Sec. No.", "ssn", 11);
    ... add more stuff (link to privacy policy, et. al.) ...
  ------------------------
  // Enhanced macro approach
  // If you have a sufficiently advanced macro system and ONE of: 
  //   higher order functions, procedural closures, functor objects
  //   or block-local macros that can recursively expand into more block-local macros
  // Then you can get the same effect. 
  // E.g. using a minimally sufficient macro system and functor objects:
  #define FORM_BLOCK(the_html)                    \            
      InputBoxFunctor     inputBox(the_html);       \ 
      DropDownBoxFunctor  dropDown(the_html);       \ 
      SubmitButtonFunctor submitButton(the_html);   \ 
      (... etc. ...)
  // (note: this would be nearly free with even a mediocre optimizer, so
  //    long as the constructors here are inlined and side-effect free)
  void build_business_form(HtmlObject& theHtmlObject) {  
    ... build headers ...
    { FORM_BLOCK(theHtmlObject); // enables 'inputBox', etc.
      if (foo && bar || snoog) {
        inputBox("First Name", "fname", 30);
        inputBox("Last Name", "lname", 30)
      }
      else { 
        inputBox("Organization Name","fname",30);
     }
     inputBox("Social Sec. No.", "ssn", 11);
    } // end FORM_BLOCK
    ... add more stuff (link to privacy policy, et. al.) ...
  }

{Again, language bloat ("linguistic creeping featuritis") would be adding a bunch of equivalent features. You ain't gonna need more than one way to do it. If you have a sufficiently advanced macro system and ONE of (higher-order functions, procedural closures, functor objects), then your language simply doesn't need block closures - you get the equivalent for free. OTOH, if you don't have a good macro system, or if you are missing all possible approaches to describe local functions (ThereIsntEvenOneWayToDoIt), then something like these block closures can make things (measurably) easier on the user. The only significant problem with using macros (esp. RealMacros) is that they're so broad in capability that (a) the patterns on the effective mechanisms for using them are quite non-obvious, and (b) a lot can be hidden in them, making it more difficult for a maintainer to comprehend what's happening just by looking at the text. Comparatively, BlockClosure?s and the equivalents capture a use-pattern in a language-feature that is easy to teach and easy to understand.}

Somebody above stated, "or you can switch to a non-brain-dead language that supports Block Closures". I don't see how lack of closures makes these kinds of programs "brain dead". I agree they may simplify a few things in a few cases, but I hardly see them as revolutionizing day-to-day programming. We've been over this in ArrayDeletionExample and ChallengeSixVersusFpDiscussion. Nobody ever showed HOF's making significant improvements, only minor ones at best. "Braindead" does not pass muster. FP fanatics have not made a good show so far. Everybody paradigm or technique probably has spots where it shines; however the ratio of benefits to additional features starts to diminish as the pile on. See ParadigmPotpourriMeansDiminishingReturns.

{"Brain dead" is awful language for describing a language in any case - it doesn't mean anything more technical than a vague disapproval from the speaker. As to whether closures provide 'significant improvements', that really depends upon your goals. Closures certainly don't add anything to computation capability (a language is or is not TuringComplete independent of closure features). They also don't add anything to communications capability (any network connection or port you can utilize with a closure, you can utilize without a closure). However, if your goals are encapsulation, modularity, or security (capability-security), closures or any equivalent (like objects) can provide (and have provided) significant benefits. What you can hide, you can change. What you can hide, you can secure. What you can hide, you can turn into a plug-in. Perhaps you don't care about encapsulation, modularity, or security, so these things are not "significant" in your narrow-minded view. But that does not mean they are not 'significant' to purposes outside your limited domain.}

{In this particular example, the advantage sought was somewhat less grand - simply to avoid rewriting 'theHtmlObject.addInputBox(...)' on each line. Block closures were simply one mechanism proposed to solve the problem, and I'm sure the speaker's "Brain dead" was an implicit jab at the various popular languages that don't allow anything equivalent... such as Java.}

With regard to Java, I agree. One cannot have a simple "print" function without per-class repetition. But this is because they tried to make it "pure OOP" and skipped useful procedural features. (This is one reason why OOP is not a superset of procedural.)

{By missing "useful procedural features", which do you mean? (The only one I can readily think of is local functions that can capture local variables.) I'm asking this more out of curiosity than any intention to criticize, though I'm curious also as to what you are describing as the "set" of procedural that Java is failing on.}

Functions that don't need a class and that can have the same scope as a class.

{You mean, like, static methods? I'm a bit curious as to what you judge to be the technical difference between a static method and a global procedure.}

 print("No dot-paths and crap, just Print and only Print.");

[Yeah, but print to what? Are you directing output to a window, a printer, a socket, or a standard output stream?] -- DaveVoorhis

In a web-oriented language, the HTML stream is usually the best default. In non-web languages, the "console" makes the best default. To select a specific device:

  print("foo",deviceHandle);

Or

 print("foo", device="toaster");

[What about general-purpose languages that are neither web-oriented or console-oriented? How is the above superior to deviceHandle.print("foo") or getDevice("toaster").print("foo"), or the following?]

 with deviceHandle {
   ...
   print("foo");
   ...
 }

The problem with "handle.verb" is that it always requires the "handle" part. If you want "verb" to stand on its own, it cant. As far as "with" statements, they have their own downside. But everybody has their preference and I don't want to get into a big fight over such here. Everybody loves their own style and hates the guts out of everyone else's style.


(Moved discussion to LessonsFromHistoryDiscussion)


See also: SubLanguage


NovemberZeroSeven

CategoryScripting


EditText of this page (last edited May 7, 2008) or FindPage with title or text search