Live data from Hacker News

Why I still Lisp

mendhekar.medium.com

121–130 of 240 posts

Re: Why I still Lisp

#121
post #8

I think Lisp is going to live forever as a niche tool for people who "get" it. I use Clojure on a daily basis. Not necessarily because it is best Lisp, but rather because I am working with Java applications and being able to reuse the same Java code I have already developed is a huge boon to me. If I was able to choose, I would be using Common Lisp. The way I use it is to quickly develop adhoc tools and PoCs and more…

How do you find REPL integration in Common Lisp compares with Clojure? I find myself blissfully productive in Clojure, and I wonder if it's the same for all Lisps

Common Lisp invented the REPL, Clojure only provides a subset of it.

Have a look at, "The Interlisp Programming Environment",

https://www.computer.org/csdl/magazine/co/1981/04/01667317/1...

Or online demos like https://youtu.be/OBfB2MJw3qg for Symbolics.

Ultimately you can try the community versions of Allegro and LispWorks.

https://franz.com/enterprise_development_tools.lhtml

http://www.lispworks.com/

Re: Why I still Lisp

#122

In other words, static typing is pointless. It has, maybe, some documentary value, but it does not substitute documentation on other invariants. For example, your invariant might be something like I’m expecting here a monotonically increasing array of numbers with a mean value of such and such and a standard deviation of such and such. The best any static type checking will let you do is “array[float]”. The rest of y…

Thank you for mentioning that and reminding me about two of my favorite projects, Eiffel and PyContract. (Is part of why DbC is so useful is because it's an extremely concise way to write assertions?)

I'd love static typing systems if they allowed for DbC-style type declarations. There are times when the thing I need to use isn't a generic int, or even a short int, but an integer three or more. That's also type information, just not type information based on the shape of the data in memory. I've never seen a type system that can make those determinations at compile time, even when working with literal numbers in source code.

Re: Why I still Lisp

#123
post #50
post #9

Earlier quoted context omitted.

I agree that testing is not a panacea, but I also don't think it is completely divorced from design. I see testing and design as cooperative processes, I agree that design is almost always the highest yielding process in any non-trivial program, but I think of testing almost as a design phase that tries to invert the design to look for weaknesses.

I suspect what he’s referring to is the fact that so many people think that, because they have 100% code coverage in their tests, they have quality software. I’ve seen so many times a team claim that with pride when it’s brutally obvious to anyone who looks that the product is broken. They just don’t seem to be able to reconcile that problem when asked about it. What you test matters every bit as much as how much you…

That's part of what I was talking about, but the original statement in the article was that rigorous testing guarantees software quality, and it also says "there is no other solution", and that is demonstrably false.

One example: A telecom product that processed phone calls. I stepped into a role on the team building the call progression handling system, which was based on an ad hoc state machine and some inter-task communication that included a combination of mutexes and message queues. It was a mess. Our testers succeeded in churning out a lot of bug reports. Not a single one of those bug reports did anything to improve the quality of the system -- even if we're not being pedantic and saying that only the patches in response to the bug reports were what might have improved the quality. Because none of the patching we could do really made any net increase in quality. The only thing that improved system quality was stepping back and re-examining the design. We changed the existing undocumented design to get rid of deadlocks by just using one inter-task mechanism (just queues, no mutexes) and by formalizing the state machines so that none of them got stranded in an unexpected state. Once the design was formalized this way we found -- by inspection, not testing -- a number of fundamental bugs in the states and transitions. After fixing those, more system testing showed some more bugs, but we were able to assess these in the context of the design and fix them at the root cause rather than just hacking on patches. The design changes also enabled automated integration testing that wasn't possible with the old ball of mud. This helped save time in allowing the [mostly manual] system testers to focus on certain areas of the system.

I've worked in other situations where lots of testing just resulted in lots of bug reports, which ended up mostly getting deferred and thus did nothing whatsoever to improve product quality.

And I've worked on software teams that were in fact producing rather high quality software, but we were building the wrong thing. It's really hard to fix bad requirements through testing.

I don't want to sound like I'm anti-testing, but I think it should be understood that testing isn't the source of quality, it's just the last phase of a process that hopefully has (non-test) mechanisms to ensure a quality product in the upstream process phases.

Re: Why I still Lisp

#124
post #69
post #43

Earlier quoted context omitted.

Honestly I’ve seen just as many disastrous codebases written in highly structured, statically typed languages. Any language with intrinsically interesting features tends to attract inexperienced (or just bad) developers who don’t appreciate the tradeoffs of their tools and design decisions, and think the tool is a panacea. This leads to disastrous codebases, and is not at all limited to lisps or dynamically typed lan…

The question isn't whether it is possible to write shitty code in a given language. Every language can be used to write shitty code. The issue is, given a person that knows the language does not know how to structure their code, what is likely going to be the outcome with regards to maintainability. My production experience is mostly with C, C++, Python and Java. As an example, consider typical Java backend applicati…

> The issue is, given a person that knows the language does not know how to structure their code, what is likely going to be the outcome with regards to maintainability.

> My production experience is mostly with C, C++, Python and Java

> On the other hand Clojure imposes exactly ZERO structure on your application. That is powerful but only if you know how to structure the application yourself, know what kind of choices you need to make and know ins and outs of various options

All I can say is I have Java, C#, C++, JavaScript professional experience and also Clojure.

From my experience, I've noticed that actually I can navigate better even a messy Clojure codebase, because the language is actually quite simple. Even if someone plasters poorly thought out macros everywhere, the rules of macros and macro-expansion are very simple and clear. I can easily figure out what they do and then start to make sense of the mess. And the REPL allows me to very quickly explore everything that is structured confusingly and hard to read.

This is not true with messy C++, C# or Java from my experience. When Java gets messy, you are now dealing with pre-processors, custom annotations, XML/reflection, magic strings, hidden circular dependencies, and ton of coupling, and those can really get out of hand. There's no simple systematic method to unraveling the mess, like there is in Clojure.

And on the data side of things, this is also true. Clojure data-model being immutable also means it can systematically be unraveled. Again, in Java, C# and C++, you have so many hidden data dependencies that can trip you up, realizing the spread of the shared data across a messy code base is quite difficult, in Clojure, the only challenge is figuring out its flow, but not its sharing.

But, to caveat, all the Clojure services I've worked on were developed while I was lead. So it's possible that having me around managed to prevent getting them in a place that cannot be salvaged. And similarly most Java, C++ or C# projects I've led also never ended up in such a rot for as long as I was around. Where as the Java, C++ and C# projects I've found to be unsalvageable mess I've generally inherited from others or people before me. I haven't yet had to inherit a Clojure project where I wasn't involved with from the start. I do wonder what would happen then. My best experience here is open source, till now, most Clojure open source code base I've explored I've found simple to unravel, but maybe open source has a higher quality bias.

Edit: One last thing that keeps me hopeful is Emacs, probably the oldest most distributed development project ever using a Lisp, and I find it pretty easy to understand its code base and add to it.

Re: Why I still Lisp

#125
post #38
post #17

> I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway). Obvious in retrospect is not the same as obvious. And such errors happen all the time, the same way without syntax checks typos happen all the time. And "caught in testing" is 2 extra steps removed from caught immediately by the syntax checker…

> What thing that violates a type check would be "perfectly fine to do"? One good example is where you might treat records or "product types" as maps Let's say you want to write a function that can capitalize all the string fields in the object passed in. In a dynamic language, you could map over the values and apply capitalization trivially. It would be a one-liner. In a static language, you'd have a few options, bu…

That's not a very compelling example - in most statically typed languages the object field names are not actually stored in member, so it makes no sense to want to do runtime operations on them, because they don't exist by the time the code is compiled.

That's why you find it hard to do in a compiled statically typed language - because it's not something that really makes any sense.

Re: Why I still Lisp

#126
post #11

> I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway). This is the static vs dynamic type debate in a nutshell. Personally I think Typescript hits a sweet spot of static typing by default with an escape hatch (via the `any` type) when you need it. I think more self-documenting code makes the trade…

It's like saying you don't need a spell checker becuase you never make mistakes. Pure hubris.

Re: Why I still Lisp

#127
post #43

Earlier quoted context omitted.

Honestly I’ve seen just as many disastrous codebases written in highly structured, statically typed languages. Any language with intrinsically interesting features tends to attract inexperienced (or just bad) developers who don’t appreciate the tradeoffs of their tools and design decisions, and think the tool is a panacea. This leads to disastrous codebases, and is not at all limited to lisps or dynamically typed lan…

You can write Perl in any language. Lack of expressive power in the language can also lead to a disaster. Sure, Java maybe doesn't have a fancy typesystem and macros, but then the same kind of clever developers use runtime reflection and bytecode manipulation which tend to end up with just as big disasters if not bigger.

Exactly my experience. Most Java code base end up using all kind of clever tricks like you said, runtime reflection, bytecode manipulation, source code pre-processing/generation, pushing all logic to configuration, abuse of compiler annotations, etc.

These actually create a bigger disaster, because unlike Lisp macros or higher order functions, they have even less structure, and are completely non-standard. I'd rather people use well thought out mechanism for extension and "cleverness" than some poor ad-hoc variant to do the same.

Re: Why I still Lisp

#129
post #17

> I have never had a static type checker (regardless of how sophisticated it is) help me prevent anything more than an obvious error (which should be caught in testing anyway). Obvious in retrospect is not the same as obvious. And such errors happen all the time, the same way without syntax checks typos happen all the time. And "caught in testing" is 2 extra steps removed from caught immediately by the syntax checker…

An example: > y = [1] ++ [2] ;; Assume types List[Int], List[Int] > y[0] ;; This should return Union[Int, None] > y[0] + 3 ;; This should fail type-checking You /know/ the list isn't empty. Sure you can argue that /maybe/ we should be using NonEmptyList, but now imagine this code. > y = [1, 2] ;; NonEmptyList[Int] > y = (filter (lambda x: x % 2 == 1) y) ;; Has to return List[Int] > y[0] ;; Union[Int, None] Again, we…

The type system is a proof system. In your examples, the types do not imply the invariants that you want.

Does this mean that the type system is faulty? No! It just means that you need to e.g. get a more expressive type system, document code properly, use smart constructors. There are many tools.

Nobody said you can trivially prove everything using type systems. So what are your examples showing?

I see your code, but I don't know what you're actually trying to do. One interpretation could be, that you want to prove that for the naturals (excluding 0), up to and including 2, there is an odd number. Can one prove this using type systems? Definitely. If you read Software Foundations, you will know how.

Re: Why I still Lisp

#130
post #71

Earlier quoted context omitted.

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 t…

I 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 becau…

[deleted]
Post reply on HN