Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Lisp certainly has many advantages, but general readability is not one of them. Maybe when talking about complex algorithms with a sufficiently designed DSL... Here's your code reformatted:

  (defun memoize (fn)
      (let ((cache (make-hash-table :test #'equal)))
      #'(lambda (&rest args)
          (multiple-value-bind (val win) (gethash args cache)
          (if win val
              (setf (gethash args cache) (apply fn args)))))))
And here is a prettier (use of TryGetValue), C# 3.0 (Func types, local variable type inference) version:

  static Func<TParam, TReturn> Memoize<TParam, TReturn>(Func<TParam, TReturn> func)
  {
      var memoDict = new Dictionary<TParam, TReturn>();
      return arg =>
      {                
          TReturn result;
          if (!memoDict.TryGetValue(arg, out result))
          {
              result = func(arg);
              memoDict[arg] = result;
          }
          return result;
      };
  }
  
The C# version is clearly longer, but less dense. Most of the noise in the C# version comes from the verbose type declarations. Almost anyone could read the C# code, provided they know that => means lambda and at least heard of a closure. That is not true of the Common Lisp version. As with everything in engineering, it is a balance. Lisp is absurdly powerful and C# is absurdly clear to read. As a programmer who does a lot of maintainence programming at work, I highly appreciate that attribute of the language.


I would format the function like this:

    (defun memoize (fn)
      (let ((cache (make-hash-table :test #'equal)))
        (lambda (&rest args)
          (multiple-value-bind (val win)
                               (gethash args cache)
            (if win
              val
              (setf (gethash args cache) (apply fn args)))))))
I'm a Lisp programmer and the C# arglist is already hard to parse with lots of noise - for me.

Second I don't look for { or ( but for the block indentation, so a { on a single line does not give me enough visual clues.

The 'return arg =>' form is not obvious to me. What does it do? Where dies TReturn come from, TParam?

Why is there }; and } ?


> Second I don't look for { or ( but for the block indentation, so a { on a single line does not give me enough visual clues.

We could argue for days about these sort of issues, and there are passionate ideas on either side of the fence.

  function
  {
    code;
    code;
  }
In that form, you have the beginning { and ending } for explicitly define the beginning and ending of the code block, and all of the code inside is also indented for good measure. Maybe it's redundant, but I don't see how it's wildly different from:

  function
    code;
    code;
> Why is there }; and } ?

Have you never programmer outside of lisp? I've never programmed in C#, but if 'arg =>' is the beginning of lambda, then it's because the definition of that line is return EXPRESSION;.

And in that case EXPRESSION = 'arg => { code }'. That's where the ';' comes from.


=> denotes a lambda. The left side is the args, the right side is the body. Lambdas are typically a single expression, but can have a statement body instead. A semicolon denotes the end of a statement.

  var square = x => x * x;

  var square = x => { return x * x; };


I don't know C#, but it seems to me that your C# version of memoize works only for functions with exactly one argument, while the CL version works for any kind of function.


Yeaaah.... It turns out that generic methods with variable arity is a sore spot for the .NET type system. The C# and F# teams are quite aware.

The typical/recommended/easy/practical solution is to just provide another overload, pack the args into an object array, and then call the original overload; completely ignoring the problem.

Alternatively, you could do some scary things with reflection...


Hmmm, I guess I should learn how to format text on hn when I want to make a point about readability. ;-)

Even so, with the caveat that you have to learn Common Lisp fist, I think the CL version is infinitely more clear and elegant. All that type information is redundant in the C# version, since in this case all you want to say about the types is that you don't care what they are.

multiple-value-bind is among the longest function name in CL, and doing something that python does much better with syntax alone, perhaps Python is the new Lisp.


"Almost anyone could read the C# code, provided they know that => means lambda and at least heard of a closure. That is not true of the Common Lisp version."

Aha. WTF? Ok, if I know what the code means, than I can read it. But that's only the case for C# and not for Common Lisp.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: