4 ms·
I 100% agree that disastrous codebases are every bit as likely in other languages. But it somehow hurts more in a lisp. I think that the syntax really is the c
by mumblemumble 6y ago
I 100% agree that disastrous codebases are every bit as likely in other languages. But it somehow hurts more in a lisp.
I think that the syntax really is the culprit. In a language like Java, the Byzantine syntax and semantics enforce some minimum level of structure on the code. It's not much, but it's something, just enough to give an experienced programmer a few extra heuristics they can use to make sense of what they're seeing. Even some things that normally drive me nuts about Java, like the way that there's no standard way to call an anonymous function (because the invocation is done through a method whose name is allowed to vary) end up being useful bread crumbs when I'm reading somebody else's code.
In a Lisp, though, everything looks more-or-less homogeneous. You can memorize the names of special forms, but, beyond that, you can't quickly tell whether some thing you're looking at is data, a function, or a macro, or what. It all blends together into a disorienting fog. You need to go trace its provenance to see how some new value is defined or constructed before you can even tell what kind of thing it is. (This, tangentially, might be a great argument in favor of lisp-2, though I haven't spent enough time in a lisp-2 to have an opinion that's worth beans.) It may, in the grand scheme of things, be only a small reduction in what you can assume without careful study. But, in an unfamiliar codebase where you don't know much to begin with, every little scrap of knowledge counts.
It all points to an insight I should have had 20 years ago when I was working in Perl: Features that make a programming language pleasant to write tend to make it unpleasant to read, and vice versa.
- admax88q 6y agoIt feels like Lisp scales vertically and Java scales horizontally. Java scales for number of developers, its idioms and ecosystem make it easier to have tons of developers on a single project. Lisp scales on the size of problem that a small close knit team can solve. Things like the macro system in Lisp encourage defining your own problem specific DSL, which is great for individual developer productivity, but bad for involving lots of developers.
- kazinator 6y agoLike the way those defclass and defmethod macros totally destroy programming at large.
- lmilcin 6y agoI spent a lot of time thinking about this. What makes Perl or Clojure such fun languages to work with is they don't impose structure on you. You can do whatever the hell you want. On the other hand result of this is the code represents basically how you think about the problem. Which would be very different for every person. Languages with frameworks like Java+Spring, Ruby+Rails etc. are less "fun" to work with because you are not given so much freedom and likely have to fill in a lot of stupid, mundane boilerplate, but are easier to maintain in team setting because developers know more or less what to expect from the code. Very likely developers come with that knowledge and if you know one Spring application you know how to move around almost any Spring application.
- vram22 6y agoIt is the frameworks that give more rigidity and boilerplate, not so much those languages themselves (although Java does have more boilerplate than Ruby). You have a lot of freedom when you program in plain Java and Ruby vs. using frameworks in those languages. And that is at least partly by design. See the Template Method design pattern, which is the basis of frameworks that use Inversion of Control (IoC).
- lmilcin 6y agoClojure frameworks are written by Clojure developers which means they don't give you structure, only tools to realize your freedom. Frameworks that give a lot of structure are beneficial to novice developer because they need that structure. In absence of supervision, the various articles on the Internet, stack exchange answers on how to write a controller or other piece of Java app, give a novice developer necessary guidance on how to structure their code. On the other hand as a more mature, knowledgeable and experience developer you may see that this structure is not perfect. It is repetitive and it has a lot of boilerplate and it does not suit well every application. You may even figure out that every application has their own perfect framework, depending on its size and other characteristics and the problem it is trying to solve. Clojure lets you design that framework and then write the application in perfect framework for your application. That framework might be some zero-code decisions (like decisions on where to put which part of the code) but can also be some set of macros up to full blown DSL. Assuming you are mature developer, you will know how to use these to solve your problem but if you don't then that's where the problem starts.
- lispm 6y agoIt may take a while to see the visual clues: indentation, formatting, structure patterns. Beyond simple lists, Lisp uses a variety of list structure patterns for language constructs. It's a bit like learning to ride a bike: initially it looks not possible to balance, steer and move forward at the same time. One thing that's not that usual is that authors can implement new language constructs themselves. Thus one may need to understand that meta-syntax level when reading and writing new constructs. A starter book like SICP thus does not use this prominent feature: it does mostly NOT define & use macros. Authors need to learn how to design new macro operators.
- lmilcin 6y agoI think it is important distinguish the necessary from accidental complexity. Macros are typically more difficult to understand but if done well that would be because they are sinking complexity from a bunch of code. For example, if a macro implements variations of repeating construct, you are removing those variations from your entire codebase and putting complexity of dealing with that into a single macro. Now, the issue is when you start creating accidental complexity. For example, there are various techniques that reduce overall complexity just by being consistent in how you do things. Using only hygienic macros or writing macros consistently in a way that allows the reader predicting what they do reduces a lot of perceived complexity. If the reader of the code does not have to understand the macro to be able to more or less predict what it does and what are basic guarantees it provides, it reduces a huge amount of complexity when you try to read and understand. If writing a macro can be compared to writing an operator in a language then all other language design rules apply. It would be a bad language that constantly surprises the user. Unfortunately, Lisp code tends to be much more abstract with lots of complex, custom operators (macros) as building blocks. If these building blocks are not clear and cannot be relied upon (ie. you don't understand how they work and can't predict what they will do) then this is much more damaging to trying to understand how the codebase works than writing unclear functions.
- lispm 6y ago> sinking complexity from a bunch of code Sometimes macros provide domain-level constructs. This creates very dense code - which can improve code understanding. One thing I would recommend to put extra effort into macros, especially with these aspects: One should write documentation what the macro expects and what it does. This way this has not to be inferred from reading the often quite complex code. Document the implemented syntax. The macro should check the syntax of the forms. Provide error messages for rejected forms.
- blackrock 6y ago> In a Lisp, though, everything looks more-or-less homogeneous. ... you can't quickly tell whether some thing you're looking at is data, a function, or a macro, or what. It all blends together into a disorienting fog. I wonder if this is a feature of Lisp, instead of a defect. When you call up a variable or a function or macro, the intent is to return some kind of result. If variables, functions, and macros all look alike, and then maybe it can be weaved more seamlessly into the code to create its own style. You can also think of a variable, as a function call with zero parameters.
- kazinator 6y ago> you can't quickly tell whether some thing you're looking at is data, a function, or a macro, or what First of all, there are elements that are unmistakably data: "abc", 123.0E-13, #(1 2 3), :keyword :symbol. The ambiguity is that a compound expression headed by a symbol could be anything. In order to to be fooled, we just have to read the entire top-level form from the outside in. Well, that's what language is. For instance, if I'm saying "Bob believes that the Earth is flat", but you only catch my speech starting on at word "the", it looks like I'm saying that the Earth is flat. To get the real meaning (the belief is attributed to Bob), you need the whole sentence. Or, until you hear the "nai" at the end of a long Japanese sentence, you have no idea that it's going to be negated.