Most active commenters
  • nine_k(4)
  • wrs(3)

←back to thread

The programmers who live in Flatland

(blog.redplanetlabs.com)
107 points winkywooster | 17 comments | | HN request time: 0.675s | source | bottom
Show context
libraryofbabel ◴[] No.46182942[source]
Or perhaps, just perhaps, the true higher-dimensional move is realizing that choice of programming language isn’t usually the critical factor in whether a project, system, or business succeeds or fails, and that obsessing over the One True Way is a trap.

It might surprise the author to learn that there are many people who:

1) Have tried lisp and clojure

2) Liked their elegance and expressiveness

3) Have read through SICP and done most of the exercises

4) Would still choose plain old boring easy-to-read always-second-best Python for 90% of use-cases (and probably Rust for the last 10%) when building a real business in the real world.

The article could really benefit from some steel-manning. Remove the cute Flatland metaphor and it is effectively arguing that lisp/clojure haven’t been universally adopted because most programmers haven’t Seen The Light in some sort of epiphany of parentheses and macros. The truth is more nuanced.

replies(15): >>46183197 #>>46183263 #>>46183285 #>>46183303 #>>46184008 #>>46185053 #>>46185956 #>>46185986 #>>46186097 #>>46186471 #>>46186553 #>>46187246 #>>46188232 #>>46191126 #>>46192256 #
1. nine_k ◴[] No.46183285[source]
Clojure is built on dynamic typing. This is pain. I wrote enough Python (pre-mypy), Javascript, and elisp to say this. Past certain size a dynamically typed codebase becomes needlessly hard to wrangle because of that. Hence the success of Python type annotations and Typescript.

Instead, the world should have seen the light of Hindley-Milner type systems, ML-inspired languages, immutability, or at least not sharing mutable state. Did Haskell fail? Hmm, let's look at Typescript and Rust.

Don't get me wrong, a Lisp is always a great and fun language, and you can write whatever DSL you might like on top of it. But the old joke that "a Lisp programmer knows the value of everything, and the cost of nothing" still has quite a bit of truth to it.

replies(6): >>46183848 #>>46184089 #>>46185450 #>>46190920 #>>46191441 #>>46192905 #
2. wrs ◴[] No.46183848[source]
On the other hand, it would be easier to add type checking to a Lisp than it was to Python or JavaScript, and I don’t know any technical reason you couldn’t. A little Googling shows it’s been experimented with several times.
replies(2): >>46184043 #>>46184471 #
3. teaearlgraycold ◴[] No.46184043[source]
That means little to a programmer unless they really want to spend thousands of hours building a type checker before starting a project.
replies(1): >>46184338 #
4. andersmurphy ◴[] No.46184089[source]
The big difference is Clojure is immutable by default.
5. wrs ◴[] No.46184338{3}[source]
Talk about moving the goalposts! Did you implement TypeScript yourself before using it?
replies(1): >>46184596 #
6. nine_k ◴[] No.46184471[source]
Well, Typed Clojure is a thing!

But the real strength of Lisp is in the macros, the metaprogramming system. And I suspect that typing most macros properly would be a bit less trivial than even typing of complex generic types, like lenses. Not typing a macro, and only typechecking the macroexpansion would formally work, but, usability-wise, could be on par with C++ template error reporting.

replies(2): >>46184582 #>>46192976 #
7. wrs ◴[] No.46184582{3}[source]
My point was that you could implement type checking with macros, not that you could type check macros. (Though that would be cool!) As opposed to having to change the language definition first (Python) or implement an entirely new compiler (TypeScript).
replies(1): >>46184940 #
8. teaearlgraycold ◴[] No.46184596{4}[source]
The parent comment implies that the tool does not exist yet.
9. nine_k ◴[] No.46184940{4}[source]
Certainly you can implement the typechecker with macros, but it should also work on macros, before expansion. That is, you likely want (-> ...) typechecked as written, not (only) as expanded, and typing errors reported on the non-expanded form.
replies(2): >>46185078 #>>46192432 #
10. andersmurphy ◴[] No.46185078{5}[source]
Right the same way the type checker should check the type checker.
11. slifin ◴[] No.46185450[source]
Plenty of ways to define complex data shapes in Clojure

Spec is definitely underrated here considering it's built into the language and has a wider scope but for most people they want the intellisense experience which you can get with clj-kondo + mailli but is not built in so most teams don't use it, fair enough

I'd like to move the goal posts though and say I want flowstorm in every (any other?!) language

I can just run the program and scrub backwards and forwards through the execution and look at all the immutable values frame by frame with a high level UI with plenty of search/autocomplete options

For program understanding there's nothing better

The fact I can program against the timeline of values of my program and create custom UI on top is crazy

One of the most mind blowing demos to me was Bret Victor's inventing on principle and having a programmable reverse debugger for your language makes those demos viable

I built an emulator recently for work that replays what happens on live locally, combined with flowstorm I can go line by line and tell you exactly what happened and why, no print statements no reruns with my own custom UI customised to our apps interesting parts

This is my appeal to anyone outside of Clojure please build flowstorm for JavaScript and or Python

The design of flowstorm is definitely helped by the fact that 95% of Clojure programs are immutable but I don't think it's impossible to replicate just very difficult

replies(1): >>46187359 #
12. nine_k ◴[] No.46187359[source]
This indeed is one of the superpowers. I hope Elixir will eventually acquire it.
13. DeadEarnest ◴[] No.46190920[source]
If we approach the question as engineers, scientifically, with numbers and studies, not anecdotes and hand-waving, then Clojure is hands down the best language in terms of productivity and bug reduction.

To this day, I know of no study that was able to demonstrate superiority of statically-typed languages - [1].

What studies clearly show, is that both in terms of productivity [2] and bug reduction [3], expressivity reigns supreme.

And Clojure is the most expressive [4] out of languages that can leverage huge ecosystems (Java and JS, soon C++ through Jank dialect).

[1] https://danluu.com/empirical-pl/ [2] PBX study from Economics of Software Quality by Caper Jones [3] https://arxiv.org/pdf/1901.10220 [4] https://redmonk.com/dberkholz/2013/03/25/programming-languag...

14. tmtvl ◴[] No.46191441[source]
The reason I switched from Scheme to Common Lisp was because I wanted type checking more than I wanted hygienic macros or case-sensitive (by default) symbols.

Being able to do something like:

  (deftype Digit ()
    '(Integer 0 9)

  (deftype Digit-Vector (&optional (length '*))
    `(Vector Digit ,length))

  (defun integer->digit-vector (integer)
    (declare (type Integer integer))
    (coerce (loop :for character :across (format nil "~D" integer)
                  :for digit := (char-digit-p character)
                  :when digit
                    :collect digit)
            'Digit-Vector))
And then have SBCL warn me if I try to use the resulting Digit-Vector in a function which wants a String (for example) is useful.
15. Xmd5a ◴[] No.46192432{5}[source]
Word. This is a problem of lisps in general, they loose information as the same "thing" traverse the various meta-layers that constitute the system. A parsed expression is not tied to its string, and the expansion of the expression, provided it is a macro, is not tied to the original expression. In the same vein: you can't easily find the source code of a lambda that was compiled/interpreted.

Of course you can do all of this, but you need to build it yourself: see rewrite-clj. If you want to build a clojure debugger that is able to display or refer to code with the same indentation the programmer is dealing with in his text editor, you need to bridge the gap between clojure expressiosn and their string representation.

Anyway I concur that reversible macros would be great. Tag the output, have those tags propagate to the input by playing the macro backwards. Complex stuff really. That's a job for category theory I guess.

https://cybercat.institute/2024/09/12/bidirectional-programm...

16. embedding-shape ◴[] No.46192905[source]
> Clojure is built on dynamic typing. This is pain. I wrote enough Python (pre-mypy), Javascript, and elisp to say this.

Probably not an absolute truth, but definitely a personal truth for you. For me, it's pretty much the opposite, static/fixed types is such a pain when you just wanna solve a problem and you know how to achieve it, all the invariants/constraints but the language tells you "No, you know what, this other person said you cannot use X for Y, so I'm gonna say no" instead of just letting me do that thing.

With that said, I still reach for Rust for about ~30% of new projects, despite the types, because some languages fit other problems better, simple as that. And still a lot more contracting gigs available for various Rust codebases who've fallen into disrepair, so one does what one can.

I feel like big codebases regardless of their size are hard to wrangle not because of the languages used, but because of the programmers having to rush through building proper abstractions, or even considering not adding so much abstractions. I've seen awful heavily typed codebases as much as I've seen awful dynamically types codebases or awful codebases not using explicit types anywhere, to me there seems to be no correlation between "awful" and "number of explicit types used".

Personally, I prefer a big codebase with lots of (good) unit tests in a dynamic program, than a that same big codebase with no unit tests and explicit static typing everywhere, especially when refactoring and needing to ensure everything (from a business logic perspective) works correctly. But again, this is my personal truth, and I'm not trying to claim it's universal.

17. embedding-shape ◴[] No.46192976{3}[source]
> Well, Typed Clojure is a thing!

I think we (the Clojure community) quickly figured out we don't really want static typing, which is a bit evident by the low uptake of Typed Clojure.

Personally I found it to A) make it a hassle for downstream consumers since your design is suddenly impacting others, because you can "lock things down" and B) have that very same effect on your own codebase, where it becomes a lot less flexible where it needs to be flexible.

Nowadays, I just use another language if I want static types, which happens sometimes but not nearly as often to say that dynamically typed languages are "dead" or whatever.