Hacker News new | ask | show | jobs
by ilrwbwrkhv 531 days ago
You wouldn't because of the repl. You would jack in and no exactly what m is.
5 comments

Yes, you would know what it is at that moment. You would not however know if what it is at that moment in time is actually correct, or what the expected shape is, without deconstructing the entire function that is the receiver of the data. That's where static types are useful - I can just hover my mouse over a function and it will show me what is the expected input, and expected output, and I do not need to read and understand the contents of the function to know if the data is correct, because it would throw an exception if it is not, like if a string suddenly becomes a number or is missing a piece of information, etc.
Theoretically, yes. And trust me, I loved static types. I use Rust for almost everything. However, the programming loop or the iteration loop that you get into with Lisp, especially with things like Common Lisp, it's not that much of a concern. But I agree with any other language which is not a Lisp, a static type system is far superior.
As nice as nrepl/cider are, doing what amounts to setting a breakpoint in the middle of a function to see what `m` looks like isn't a replacement for knowing the type without executing code. It's just something we put up with.
yeah as I also commented on the sibling comment the real thing here is that the way you program a clojure application or a common Lisp application especially because I used to use Steelbank common Lisp so I can talk about that is you immediately go into the REPL and you jack in it's actually a little bit difficult in clojure to do that compared to common lisp.

But the mental model is fundamentally different. It's not like you write a bunch of code, set a breakpoint and see what things are. You essentially boot up a lisp image and then you make changes to it. It's more like carving out a statue from a piece of rock rather than building a statue layer by layer.

I've been using Clojure for a while and I rarely ever wonder "what 'm' is" - that almost never happens, despite the language being dynamically typed.

Data shapes in Clojure typically explicit and consistent. The context usually makes things quite obvious. Data is self-describing - you can just look at a map and immediately see its structure and contents - the keywords serve as explicit labels and the data, well... is just fucking data. That kind of "data transparency" makes Clojure code easier to reason about.

In contrast, in many other PLs, you often need to know the class definition (or some other obscured shit) to understand what properties exist or are accessible. The object's internal state may be encapsulated/hidden, and its representation might be spread across a class hierarchy. You often can't even just print it to see what's inside it in a meaningful way. And of course, it makes nearly impossible to navigate such codebases without static types.

And of course the REPL - it simply feels extremely liberating, being able to connect to some remote service, running in a container or k8s pod and directly manipulate it. It feels like walking through walls while building a map in a video game. Almost like some magic that allows you to inspect, debug, and modify production systems in real-time, safely and interactively, without stopping or redeploying them.

Not to mention that Clojure does have very powerful type systems, although of course, skeptics would argue that Malli and Spec are not "true" types and they'd be missing the point - they are intentionally different tools solving real problems pragmatically. They can be used for runtime validation when and where you need it. They can be easily manipulated as data. They have dynamic validation mechanisms that static types just can't easily express.

One thing I learned after using dozens of different programming languages - you can't just simply pick one feature or aspect in any of them and say: "it's great or horrible because of one specific thing", because programming languages are complex ecosystems where features interact and complement each other in subtle ways. A language's true value emerges from how all its parts work together, e.g.,

- Clojure's dynamic nature + REPL + data orientation

- Haskell's type system + purity + lazy evaluation

- Erlang's processes + supervision + fault tolerance

What might seem like a weakness in isolation often enables strengths in combination with other features. The language's philosophy, tooling, and community also play crucial roles in the overall development experience.

If one says: "I can't use Clojure because it doesn't have static types", they probably have learned little about the trade they chose to pursue.

With static types, I don't have the use the repl at all, I can simply hover over it in my editor.
Correction: It's not that you "don't have to use the REPL"; you simply cannot even have it in that case. REPL-driven development is quite a powerful technique, and no, "many other languages too" don't have it. For it to be "a true REPL," it must be in the context of a homoiconic language, which Clojure is.

Sure, static typing is great, but perhaps you have no idea what it actually feels like - spinning up a Clojurescript REPL and being able to interactively "click" and "browse" through the web app programmatically, controlling its entire lifecycle directly from your editor. Similarly, you can do the same thing with remote service running in a kubernetes pod. It's literally like playing a video game while coding. It's immensely fun and unbelievably productive.

With a REPL-connected editor (and most have a way to do this), you can simply hover over it in your editor as well. Even though most languages can have a REPL today, few integrate it in the development experience the way lisps do.
The compiler should know it for you, so you cannot get it wrong no matter what. The REPL here is a band-aid not a solution.

I mean, I love Clojure, and used it for personal and work projects for 10+ years, some of which have hundreds of stars on github. But I cannot count the time wasted to spot issues where a map was actually a list of maps. Here Elixir is doing the right thing - adding gradual typing.

> But I cannot count the time wasted to spot issues where a map was actually a list of maps.

Sorry, I'm having hard time believing that. I don't know when was the last time you've used the language, but today there are so many different ways to easily see and analyze the data you're dealing with in Clojure - there are tons of ways in CIDER, if you don't use Emacs - there are numerous ways of doing it in Calva (VSCode) and Cursive (IntelliJ), even Sublime. There are tools Like Portal, immensely capable debuggers like Flowstorm, etc. You can visualize the data, slice it, dice it, group it and sort it - all interactively, with extreme ease.

I'm glad you've found great fondness for Elixir, it is, indeed a great language - hugely inspired by Clojure btw.

You still don't need to bash other tools for no good reason. It really does sound fake - not a single Clojure developer, after using it for more than a decade, would call a Lisp REPL "a band-aid and not a solution". It smells more like someone with no idea of how the tool actually works.

Maybe it's so. Or maybe you run my code in your deps. As you can see, there is at least one Clojure dev who thinks so.

I found spec very useful and damn expressive (and I miss it in other languages), but again that's runtime. I know Rich says such errors are "trivial", but they waste your time (at least mine).

To each their own. Some people (not me) say that Rust's pedantic compiler feels like bureaucratized waste of time akin passing through medieval Turkish customs. For me personally, working with Clojure dialects feels extremely productive. Even writing in Fennel, which is not Clojure, but syntactically somewhat similar is much faster for me than dealing with Lua. Even when I have to write stuff in other PLs, I sometimes first build a prototype in Clojure and then rewrite it. Although it sounds like spending twice the effort, it really helps me not to waste time.
For a single run of your code. That's absolutely no guarantee that m will always have the type, no matter how you got to the call.
What do you mean? Clojure is strongly typed language - every value always has a definite type. It's not like Javascript. Types in Clojure are fixed and consistent during runtime, they just aren't declared in advance.
Do you think there's only one path to your function? There could be thousands in a big system. The type of the value you'll get will depend on the path you call it from. Even if it's only one path, you could easily have code doing stuff like this:

    if x > 10:
        call_my_function 10
    else:
        call_my_function "foo"
Can't you see that unless you test every path, you won't know what type you will receive??
Your contrived example is a bad smell in ANY language. No sensible coder ever writes a function that accepts both numbers and strings - handling multiple types should be done through proper polymorphic constructs, not arbitrary conditional branches.

There's a wide spectrum of correctness guaranties in programming - dynamic weak, dynamic strong, static, dependent, runtime validation & generative testing, refinement types, formal verification, etc.

Sure, if your domain needs extreme level of correctness (like in aerospace or medical devices) you do need formal methods and static typing just isn't enough.

Clojure is very fine, and maybe even more than just fine for certain domains - pragmatically it's been proven to be excellent e.g., in fintech and data analysis.

> Can't you see that unless you test every path ...

Sure, thinking in types is crucial, no matter what PL you use. And, technically speaking, yes, I agree, you do need to know all paths to be 100% certain about types. But that is true even with static typing - you still need to test logical correctness of all paths. Static typing isn't some panacea - magical cure for buggy software. There's not a single paradigm, technique, design pattern, or set of ideas that guarantee excellent results. Looking at any language from a single angle of where it stands in that spectrum of correctness guaranties is simple naivety. Clojure BY DESIGN is dynamically typed, in return it gives you several other tools to help writing software.

There's an entire class of applications that requires significantly more effort and mental overhead to build using other languages. Just watch some Hyperfiddle/Electric demos and feel free to contemplate what would it take to build similar things in some other PL, statically typed or whatnot. https://www.youtube.com/watch?v=nEt06LLQaBY

What are you on about, I didn't say anything about panaceas or what not... just said the obvious: in a language without static typing you just can't assume the type of the argument at all.

> And, technically speaking, yes, I agree, you do need to know all paths to be 100% certain about types.

Yes, thank you.