Hacker News new | ask | show | jobs
by gloob 5458 days ago
If you haven't seen Trusting Trust already, you should read it: http://cm.bell-labs.com/who/ken/trust.html . It's short and smart.

The brief summary is, of course, you can almost never really know "logically what's happening", even in nice reliable languages like Java or Python or Haskell or Ruby or C.[0]

The easy solution (and the one that every working programmer makes every day) is to say: well, let's assume that the compiler is non-malicious and has relatively few bugs that will impact me. That's a fair response.

Next we run into the fact that just because you call a function, and that function happens to be called add(x,y), doesn't actually mean that the function will return the sum of x and y. In the absence of further evidence, it could be an HTTP server or fill your hard drive with infinite copies of Das Kapital or anything at all. The normal solutions proposed to this are (1) read the documentation, or (2) read the code. Those are both workable solutions.

Then macros come into the picture. I fail to see how the already-heavily-used techniques of (1) reading documentation and (2) reading the implementation of the macro, which seem to be at least tolerable and mostly workable with functions suddenly fall apart with macros.

[0] C and C++ have the preprocessor and macros anyway, which "change your program logically...prior to compilation", so I won't mention them further in this comment.

1 comments

Yeah, my distrust for macros is mostly informed by my experience with C/C++ preprocessors, which have led to really annoying facepalms in my personal history.

But, as I said, I haven't actually used macros for a large project so maybe they're more workable than I give them credit for.

When I think of macros, I'm generally thinking about getting a syntax tree at runtime and doing transformations on it.

The closest thing related to this in a mainstream language is LINQ in .NET. When passing a lambda expression, you can either get a method reference that you can execute, or you can get a syntax tree of that expression.

It is effectively used in Linq to Sql to query databases using the same syntax you would use for querying in-memory collections of objects. You can also teach it to do pretty sweat things, like doing data transformations using the GPU. Check out this project for instance: http://brahma.ananthonline.net/

Also, macros are not necessarily executed at compile-time as in C/C++ - in Lisp a macro has no boundaries to when or how it gets executed. A macro is just a piece of code that can call normal functions and that returns a piece of code that will then get executed.

One common use-case is for achieving lazy evaluation in a language that isn't lazy and so most macros are trivial to understand. But you can have other use-cases, like in the above example with Linq, where the piece of code getting generated depends on runtime conditions.

I do agree that macros can make code harder to understand, but then again, I've seen horribly over-engineered, poorly implemented and unreadable pieces of Java code and so I think it all comes down to having a culture for code readability than about having a language that forces you to do things in a certain way.