Live data from Hacker News

Why I still Lisp

mendhekar.medium.com

111–120 of 240 posts

Re: Why I still Lisp

#111
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…

> And such errors happen all the time, the same way without syntax checks typos happen all the time.

No they don't, because syntax is richly varied and nested, even in programs whose "type story" is bland.

For instance, in some numerical program, there are lots of opportunities to make typos in the syntax, but the type of just about everything may be either float or else array of float (possiby string, if it has any error-handling code with messages, and bool if there are some logical operations).

If you pass an array of float where a scalar float is expected, such that an error occurs, and if you don't catch this in your testing, it means you're not testing the code.

Untested code is of a dubious status, even if it compiles with a static type system; testing is not negotiable.

Note that functions like sin and cos have exactly the same type signature, yet it is disastrous if you mix them up. Static type checking doesn't help. Static type checking also doesn't help with mixed up variables: calling f(x, y) which should have been f(y, x) or something else, where x and y have the same type.

In programs, chunks of code that are put together into the same module or function often work with multiple instances of the same type. It's more important not to mix up those instances, than to worry about type errors. The code could be wrong in all sorts of ways, yet statically check.

That's where you need to step up the the argument into "True Scotsman's type systems territory": a sufficiently advanced type system can encode all those properties that prevent the mixups that the everyday type system doesn't. Yeah, well, nobody uses that; nobody understands it outside of a narrow slice of academia. Examples of the technique are such that encoding even a trivial property like "list is in sorted order" results in an a considerable increase of program complexity all concentrated in one place, and less easy to understand than a set of test cases against the obvious program. Yet, it doesn't eliminate the need for testing; nothing does. There can now be a bug in the way the desired property was encoded into the program. Perhaps the sorted property was correctly encoded, but it should have been descending order. The test case will catch it.

When you have a language that is available at compile time, you can execute test cases as part of compilation. Test cases are therefore static checks. Anything happening at build time is a static check. Heck, how a git commit message is formatted is a static check, if a repository commit hook validates it.

Re: Why I still Lisp

#112
> As a programmer, I carry around invariants (which is a fancy name for properties about things in my program) in my head all the time.

The author has probably not worked on large code bases or even in teams.

I often forget even my own code's purpose six months after having written it, let alone code written by teams of hundreds of people and millions of lines of code.

Dynamically types will kill you in such (common) situations.

> In other words, static typing is pointless.

Yes, author is definitely very young.

There are two types of programmers: programmers who know that static typing is the only sane way to write robust, scalable, maintainable code, and programmers who haven't been in this profession for long enough.

Re: Why I still Lisp

#113
post #106
post #101

Earlier quoted context omitted.

See: http://www.flownet.com/ron/lambda-calculus.html

That didn't answer my question. Let me rephrase it: When would you use (define (self-apply fn) (fn fn)) in an actual, real-life situation (outside of teaching lambda calculus)?

Personally, I consider teaching the lambda calculus to be a real-life situation. Teaching the lambda calculus is no less real to me than any other part of my life. But OK, self-apply is not something you're likely to see in production code, and neither are Church numerals, which is where this problem shows up for real. But MAP, REDUCE, and APPLY are, and they all have the same problem as self-apply: their static types are infinite.

This is a general problem with all static type systems. There are only two possibilities: either your type system is Turing complete or it is not. If it is not, then there are things that one might reasonably want to express that cannot be expressed, and if it is, then it is undecidable. Static typing is either constraining, or it is a Turing Tarpit (https://en.wikipedia.org/wiki/Turing_tarpit).

Re: Why I still Lisp

#114
post #43
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…

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.

Re: Why I still Lisp

#115
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…

Yes, frameworks are kind of like a smarter programmer starting you off with a code base already, so if you don't really know what you're doing you can't mess it up as much. I've still seen people mess it up, and generally it's when the developers begin to "play" with things they don't understand, like bringing in Aspect J, writing custom annotations, slowly moving logic to configuration files, starting to dynamically load modules in/out.

It's a bit of a struggle because an experienced dev will hate the framework, as they know better, but then as less experienced dev work on the code base it'll degenerate in a way the framework would have survived longer due to availability of documentation and Stackoverflow Q/A.

Re: Why I still Lisp

#116
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 hit into type issues.

Re: Why I still Lisp

#117

> As a programmer, I carry around invariants (which is a fancy name for properties about things in my program) in my head all the time. The author has probably not worked on large code bases or even in teams. I often forget even my own code's purpose six months after having written it, let alone code written by teams of hundreds of people and millions of lines of code. Dynamically types will kill you in such (common)…

> I started serious programming in my teens in BASIC on a ZX Spectrum+, although I had previously dabbled in (hand-) writing Fortran programs

> I ended up studying Programming Languages at Indiana University with Dan Friedman (of The Little Lisper / The Little Schemer fame). It was my introduction to Scheme (and the world of Lisp.) I finally knew that I had found the perfect medium to express my programs in. And it has not changed in those last 25 years

Author seems to have over 25 years of experience.

Re: Why I still Lisp

#118

> But it has now taken a new interpretation in the last couple of decades: Static typing is a form of compile-time error checking, so it will help you produce better quality code. It is as if static typing is a magical theorem prover that will verify some deep properties of your program. This is where I call bullsh*t . [ sic ] I have never had a static type checker (regardless of how sophisticated it is) help me prev…

> That's an anecdote; facts are that serious critical bugs that lead to privilege escalation and loss of data have existed in codebases that would surely have been caught by various static type systems.

Do you have any non-strawman examples of this (e.g. C security holes are strawman).

Re: Why I still Lisp

#119
post #38

Earlier quoted context omitted.

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

There is no limit on the number of problems easier to solve using dynamically typed languages. For an individual hacker, dynamically typed languages make a lot of sense. I don't forget what types my functions accept as I am writing a program. All static typing can accomplish is slow me down when I'm trying to bang out something. This is why Python (for instance) is loved by data scientists, researchers, and startups.…

The auto-completion is purely Python's dynamic-dispatch problem though. Lisp has symbol-based auto-completion which doesn't suffer from the same issue (search is a different story!).

This sounds terribly controversial, but I would love to know /when/ the static-typing pays off compared to a looser approach like sound-gradual typing (where you basically export types at a boundary and make sure that you don't violate those).

I do wonder whether a better approach is to prove your code in some other language (say Z3, Agda, Coq) - then implement it in something else, making sure you can prove isomorphism. This has quite a few benefits; you're not tied to a constructive proof on the type-level ala Haskell, Rust, Go etc... and you can automate a large part of trivial proofs.

Re: Why I still Lisp

#120
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…

I reject the claim that it is reasonable to capitalize all the string fields of arbitrary objects.

But it can still be done, like you mention, by using reflection. Now, you claim that it is unsafe. This is untrue, it depends how the reflection works. Even if it were unsafe, it would be no worse than in Python, where you get a TypeError or in JavaScript where you get undefined, which probably leads to a crash later.

For an example of reflection which is safer than just a string-dictionary like JavaScript or Python, see GHC.Generics. It works by compile-time generating a type safe structure which is not just a dictionary. The function you want would have a signature involving this constraint, "Generic x => x -> x", which means it works for any "object" implementing the Generic interface.

Now you may say "but I asked for a function that handles all objects", but I don't see the point. In statically typed languages you get an additional option of using generics when you want. It doesn't make sense to me to require all types to implement reflection when you don't actually need it everywhere.

Post reply on HN