Yes you are. First of all there isn't such a thing as "strict typing", types are either static/dynamic and/or strong/weak. I suppose you meant Elixir has no static types. It is however a strongly typed language.
And just like it usually happens, static typing enthusiasts often miss several key insights when confronting dynamically typed languages like Clojure or Elixir (which was inspired by ideas implemented in Clojure).
It's not simply "white" and "black", just like everything else in the natural world.
You have to address:
- Runtime flexibility vs. compile-time safety trade-offs — like most things, things have a price to it, nothing is free.
- Different error handling philosophies. Sometimes, designing systems that gracefully handle and recover from runtime failures makes far more resilient software.
- Expressiveness benefits. Dynamic typing often enables more concise, polymorphic code.
- Testing culture differences. Dynamic languages often foster stronger testing practices as comprehensive test suites often provide confidence comparable to and even exceeding static type checking.
- Metaprogramming power. Macros and runtime introspection enable powerful abstractions that can be difficult in statically typed languages.
- Gradual typing possibilities. There are things you can do in Clojure spec that are far more difficult to achieve even in systems like Liquid Haskell or other advanced static type systems.
The bottom line: There are only two absolutely guaranteed ways to build bug-free, resilient, maintainable software. Two. And they are not static vs. dynamic typing. Two ways. Thing is - we humans have yet to discover either of those two.
You act like OP has never experienced dynamic type programming.
They clearly said they "can't go back to" it, meaning they've experienced both, are aware of the trade-offs, and have decided they prefer static types.
> Gradual typing possibilities. There are things you can do in Clojure spec that are far more difficult to achieve even in systems like Liquid Haskell or other advanced static type systems.
That's great for clojure and python and PHP, but we're not talking about them.
You act as if I said anything about anyone's experience. "they prefer static types" can mean a whole lot of things - there's type inference, soundness, turing-completeness, type classes, GADTs, higher-kinded types, dependent types, gradual typing, structural vs nominal typing, variance annotations, type-level programming, refinement types, linear types, effect systems, row polymorphism, and countless other dimensions along which type systems vary in their expressiveness, guarantees, and ergonomics.
Dynamic typing also varies - there's type introspection, runtime type modification aka monkey patching, different type checking strategies - duck typing & protocol checking, lazy & eager, contracts, guards and pattern matching; object models for single & multiple dispatch, method resolution order, delegation & inheritance, mixins, traits, inheritance chains, metaprogramming: reflection, code generation, proxies, metacircular evaluation, homoiconicity; there are memory and performance strategies: JIT, inline caching, hidden classes/maps; there are error handling ways, interoperability - FFI type marshaling, type hinting, etc. etc.
Like I said already - things aren't that simple, there isn't "yes" or "no" answer to this. "Preferring" only static typing or choosing solely dynamic typing is like insisting on using only a hammer or only a screwdriver to build a house. Different tasks call for different tools, and skilled developers know when to reach for each one. Static typing gives you the safety net and blueprints for large-scale construction, while dynamic typing offers the flexibility to quickly prototype and adapt on the fly. The best builders keep both in their toolbox and choose based on what they're building, not ideology.
In that sense, the OP is wrong - you can't judge pretty much any programming language solely based on one specific aspect of that PL, one has to try the "holistic" experience and decide if that PL is good for them, for their team and for the project(s) they're building.
Runtime flexibility is not restricted to dynamically typed languages, it just happens to be less available in some of the popular statically typed languages.
Error handling, expressiveness, testing culture, meta-programming and gradual typing have nothing to do with static vs dynamic typing.
The main "advantage" of dynamically typed languages is that you can start writing code now and not thing about it too much. Then you discover all the problems at runtime...forever.
Statically typed languages force you to think about what you are doing in advance a lot more which can help you avoid some structural issues. Then when you do refactor computer helps you find all the places where you need to change things.
Dynamically typed languages force you to write more tests that are not required in statically typed languages and that might prompt you to write other tests but if also increases the chance you just give up when you start refactoring.
Finally, after some time has passed and few updates have been applied to the language and libraries, you may not have a working project anymore. With statically typed languages you can usually find and fix all the compile errors and have the fully working project again. With dynamically typed languages, you will never know until you explore every line of code, which will usually happen at runtime and on the client computer.
Sure you're right on most of this, but allow me a slight pushback here. I am sorry, I am inclined to use Clojure/Lisp in my examples, but only because of its recency in my toolbelt, I could probably come up with similar Elexir examples, but I lack intimate familiarity with it.
- Dynamic languages can harbor bugs that only surface in production, sometimes in rarely-executed code paths, yes. However, some dynamically typed languages do offer various tools to mitigate that. For example, take Clojurescript - dynamically/strongly typed language and let's compare it with Typescript. Type safety of compiled Typescript completely evaporates at runtime - type annotations are gone, leaving you open to potential type mismatches at API boundaries. There's no protection against other JS code that doesn't respect your types. In comparison, Clojurescript retains its strong typing guarantees at runtime.
This is why many TS projects end up adding runtime validation libraries (like Zod or io-ts) to get back some of that runtime safety - essentially manually adding what CLJS provides more naturally.
If you add Malli or Spec to that, then you can express constraints that would make Typescript's type system look primitive - simple things like "The end-date must be after start-date" would make you write some boilerplate - in CLjS it's a simple two-liner.
- Static type systems absolutely shine for refactoring assistance, that's true. However, structural editing in Lisp is a powerful refactoring tool that offers different advantages than static typing. I'm sorry once again for changing the goalposts - I just can't speak specifically for Elixir on this point. Structural editing guarantees syntactic correctness, gives you semantic-preserving transformations, allows fearless large-scale restructuring. You can even easily write refactoring functions that manipulate your codebase programmatically.
- Yes, static typing does encourage (or require) more deliberate API design and data modeling early on, which can prevent architectural mistakes. On the other hand many dynamically typed systems allow you to prototype and build much more rapidly.
- Long-term maintenance, sure, I'll give a point to statically typed systems here, but honestly, some dynamically typed languages are really, really good in that aspect. Not every single dynamic language is doomed to "write once, debug forever" characterization. Emacs is a great example - some code in it is from 1980s and it still runs perfectly today - there's almost legendary backward compatibility.
Pragmatically speaking, from my long-term experience of writing code in various programming languages, the outcome often depends not on technical things but cultural factors. A team working with an incredibly flexible and sophisticated static type system can sometimes create horrifically complex, unmaintainable codebases and the opposite is equally true. There's just not enough irrefutable proof either way for granting any tactical or strategic advantage in a general sense. And I'm afraid there will never be any and we'll all be doomed to succumb to endless debates on this topic.
> The bottom line: There are only two absolutely guaranteed ways to build bug-free, resilient, maintainable software. Two. And they are not static vs. dynamic typing. Two ways. Thing is - we humans have yet to discover either of those two.
That's true but some languages don't let you ship code to prod that multiplies files by 9, or that subtracts squids from apricots
> that multiplies files by 9, or that subtracts squids from apricots
I don't understand why when someone mentions the word "dynamic", programmers automatically think javascript, php, bash or awk. Some dynamically typed PLs have advanced type systems. Please stop fetishizing over one-time 'uncaught NPE in production' PTSD and acting as if refusing to use a statically typed PL means we're all gonna die.
> Over time you will probably feel drawn to both, for different reasons
I agree 100%. At first I liked C# and Java types, but then I moved to Python and I was happy. Learning some Typescript pulled me back into the static typing camp, yet then I discovered Clojure it revealed to me how needlessly cumbersome and almost impractical TS type system felt to me in comparison. Experimenting with Haskell and looking into Rust gave me a different perspective once again. If there's a lesson I've learned, it's that my preferences at any point in life are just that - preferences that seldom represent universal truths, particularly when no definitive, unambiguous answer even exists.
> smalltalk and common lisp don't qualify as popular languages.
Sure, Smalltalk isn't, but Lisp is a different story. In this context I assume we all mean to say "Lisp" and not "Common Lisp" specifically.
Lisp (as the entire family of PLs) is quite massively popular.
Standard rankings have major blind spots for Lisp measurement, they miss things like, for example, Emacs Lisp is everywhere. There's tons of Elisp on GitHub alone, and let me remind you, it's not a "general-purpose" language, its sole function is to be used to configure a text editor and there's mind-boggling amount of it out there. AutoLISP is heavily used in CAD/engineering but rarely discussed online. Many Lisp codebases are proprietary/internal. Also, dialect fragmentation artificially deflates numbers when measured separately - many rankings consider them different languages.
If you count all Lisp dialects together and include: Emacs Lisp codebases, AutoLISP scripts in engineering, Research/academic usage, Embedded Lisps in applications, Clojure on JVM and other platforms - babashka scripts, Flutter apps, Clojurescript web apps, etc;
...Lisp would likely rank much higher than typical surveys suggest - possibly in the top ten by actual lines of code in active use.
No, you're not alone. After learning Lisp, structural editing and REPL-driven-development, I just don't feel like needing to learn new languages no matter how powerful they seem to be. Lisps like Clojure are highly pragmatic and offer something fundamentally different from the endless parade of syntax-heavy languages that dominate the mainstream.
Once you've experienced the fluidity of paredit or parinfer, where you're editing the structure of your code rather than wrestling with textual representations, going back to manually balancing brackets, fixing indetnation and carefully placing semicolons feels like reverting to a typewriter after using a word processor. The code becomes malleable in a way that's hard to appreciate until you've lived with it.
And the REPL changes everything about how you think and work. Instead of the write-compile-run-debug cycle, you're having a conversation with your running program. You can poke at functions, test hypotheses, build up solutions incrementally, and see immediate feedback. It's exploratory programming in the truest sense - you're not just writing code, you're discovering it.
The homoiconicity - code as data - means you're working in a language that can easily reason about and transform itself. Macros aren't just text substitution; they're proper AST transformations. This gives you a kind of expressive power that most languages can't match without tremendous complexity.
So when the latest trendy language appears with its new syntax and novel features, it often feels like rearranging deck furniture. Sure, it might have nice type inference or clever concurrency primitives, but you're still stuck in the old paradigm of fighting syntax and losing the conversational flow of development.
You've tasted something closer to the pure essence of computation, and it's hard to go back.
> i am interested in clojure, but i am put off by it using the java run time
Clojure runs not only on Java - you have Clojurescript, you have babashka and nbb, you have Clojure-Dart — if interested in building Flutter apps, you can even use Clojure with Python libs. If you need to target Lua, there's Fennel, which is similar as it's inspired by Clojure.
For me - Clojure is a hands-down best medium for data manipulation - there's just nothing better out there to explore some data - investigate APIs; sort, group, dice and slice any kind of data - CSVs, JSON, etc. Nothing else simply can match the joy how one could incrementally build up complex data transformations, it makes it incredibly productive for the "let me just quickly check this shit" scenarios that can easily turn into full analyses. REPL-driven nature of it makes it so much fun - you feel like you're playing a videogame.
I honestly wish every programmer knew at least some Clojure. I lost count of how many times I gave up on figuring out complex jq syntax and reached for Clojure instead. Or the times I'd quickly build a simple Playwright script for reproducible web-app bug trapping or quick data scraping that saved me hours of frustration and manual clicking.
> I've always found the Chrome/Firefox vimlike browser plugins to be such second class citizens
Bro, you have no idea - vimium-c is absolutely amazing, I just can't really imagine my life without it. Toggling tab pinning and muting, moving tabs, grouping tabs, quickly finding a tab, extracting a tab into a window, joining windows, etc. - if you even try doing all that without modality, you'd have to keep learning new keybindings and memorize them.
The worst part of that is that keybindings would change. For me - font size change on a browser page works the same way as in my editor, and the terminal, and it is the same in every browser that vimium supports.
You basically need to ask yourself if you really want to keep learning and memorizing various, sometimes absolutely unrelated and difficult to reach key bindings that almost invariably require you holding modifiers, sometimes wasting your mental energy on subtleties between ctrl/alt/cmd/shift+key or you'd rather sit down, spend some time and take a systematic approach that will make your life so much easier.
> window managers are always second class on Mac/Windows, that never stuck either.
I don't use Windows much, but Yabai for Mac is really good. I've procrastinated on trying it for a long time, when I finally tried, it took me literally fifteen minutes to start. Then I hooked up my Hammerspoon config and added some key bindings. I'm very happy about it. Like for example, I've been so annoyed by Zoom that always scatters its windows around all my monitors, now I have "group Zoom windows" feature, and it's really nice.
Emacs was always modal. Emacs is inherently a modal text editor - keychords are modal, transients are modal, isearch is modal, repeat-mode is modality. The only thing the idea of vim-navigation brings on top of all that is a simple, structured and memorable language/grammar to deal with modality, that's all what it is.
Both approaches are modal; Vi just codified its modality into a more learnable, linguistic system while Emacs distributed its modality across context-sensitive mini-languages.
Emacs has this same power scattered throughout - the kill-ring, rectangle operations, keyboard macros - but Vi's genius was packaging it into a coherent, immediately graspable system that feels less like using a computer and more like speaking a concise language about text.
The best thing about learning that language is that you can apply it anywhere where keyboard input is still relevant - your browser, your terminal, your entire system - e.g. you can control your music, jump between apps, navigate between windows, etc. using only the home row keys.
> The best thing about learning that language is that you can apply it anywhere where keyboard input is still relevant - your browser, your terminal, your entire system - e.g. you can control your music, jump between apps, navigate between windows, etc. using only the home row keys.
I guess maybe with some configuration? A lot of emacs keybindings work out of the box in most text boxes I can think of, including this one I'm typing into right now.
If the justification about VI is that it's more learnable, then that's hardly a reason for me to switch - I've already learned emacs.
Although I do think it's a stretch to call it "immediately graspable"
> that's hardly a reason for me to switch - I've already learned emacs.
Most people think of keyboard input as a linear, one-dimensional approach to text editing, that's what you're doing here. Vim-style navigation offers something fundamentally different - a spatial "language" that maps directly to how we conceptualize our workspace.
- C-x 4 becomes a prefix for file operations in other windows
The logic breaks down quickly. There's no consistent mental model connecting these commands. C-x 4 abandons the splitting metaphor entirely, becoming a namespace for "other window" operations rather than a window count.
Doom Emacs's vim-inspired approach creates a coherent spatial language:
- SPC w s - split
- SPC w v - vertical split
- SPC w h/j/k/l - jump left/down/up/right
The hjkl directional keys mirror vim's movement commands, creating a consistent spatial metaphor. Your fingers learn to "think" in terms of physical direction rather than arbitrary key combinations.
This spatial approach transforms window management from memorizing disconnected shortcuts into developing spatial intuition. Instead of thinking "what was that split command again?", you think "I want to move right" and your fingers naturally press 'h'. The keybindings become an extension of spatial reasoning rather than abstract symbol manipulation.
This principle extends beyond window management - vim's entire philosophy treats text editing as navigation through dimensional space, making complex operations feel natural and memorable.
And like I said, this model can easily be extended beyond just the text editor - you can navigate things in your browser, terminal, etc. Why would I even try to learn and memorize new keyboard shortcuts e.g., every time I install a new music player? I can control volume with j/k and previous/next song with h/l in any music player.
> I do think it's a stretch to call it "immediately graspable"
But it is though. It is very simple grammar. Anyone who's not mentally deficient can understand and learn it quickly. Building the muscle memory takes time, yes, but the idea itself is absolutely graspable. I remember, I hated myself for about a week. After that, my keyboard life was never the same.
Yup, I know what you're talking about. Evil-mode in Emacs actually adds so many nice features and simplifies so many things outside of text editing, yet I've never seen anyone who dislikes modal editing actually install evil-mode and keep it disabled by default. Them Emacs veteran "nevervimmers" are missing out.
For some reason Emacs veterans keep waging wars against modality without acknowledging that Emacs is inherently modal - key chords create temporary modes, transient keymaps maintain states, the minibuffer is a distinct mode, and even fundamental concepts like the mark-active state and recursive editing demonstrate that Emacs has always embraced context-dependent behavior rather than being truly modeless.
My team been discussing how Jira is the most-hated tool in the survey.
When I was less experienced, I despised Jira with an intensity that I didn't hesitate to express. My hatred burned with religious fervor, reaching such heights that my team lead once wrote on the whiteboard: "X days since [my-name] swore at Jira," promising pizza for the entire team if we reached two weeks. That pizza never materialized.
Eventually, I came to a realization that would shift my perspective entirely. Throughout my career, there would be countless hills to die on - tools, programming languages, ideas, and techniques that I simply couldn't avoid. While I couldn't control their presence, I could control my approach to them.
Jira deserves its criticism for legit reasons. The UI lacks intuitive flow, it's non-linear, keyboard navigation is cumbersome, simple tasks require clicking and clicking more, checking status or looking up information become unnecessarily complex.
But then, "a problem cannot be solved from the same level of consciousness that created it.", right? So, instead of fighting the tool, I decided to minimize my interaction with its problematic interface. I've found go-jira, a CLI tool that transformed my work. I even developed custom integrations for my editor¹, so I can do thing even faster. Ping me if you're an Emacs user, I'll show you.
Now I can search through Jira efficiently, hover over ticket references like SAC-12345 in my notes and quickly get a ticket description, instantly convert that text into a shareable URL for teammates, etc.
Don't just hate things blindly - do something about it, find workarounds.
Let's think of some hypothetical (close to practical) scenario example. Let's say I need a retrieval of fully-qualified name for a function at point.
In Emacs, I can start prototyping advising function in a scratch buffer changing the behavior of lsp--symbol-information (or related) function. I can try it out right there. I can easily debug, profile, enable and disable this feature without ever having to restart Emacs. Hell, I wouldn't even have to save it — it's all dynamic, all in-place, it's like playing a videogame.
In Neovim, I'd have to create/modify a Lua file. Find the right LSP handler to override. Write the override function. Reload the editor (losing my state), or source the file. There's no easy way to temporarily test without affecting my config. Sure, one can use Fennel for replicating REPL-driven development, but that still be limited compared to Emacs — no advice system; can't easily revert (no advice-remove like stuff); limited introspection; scope issues — need to manage original function references manually; harder discovery — no describe-function like stuff.
In VSCode to get something like that you'd need to create an entire extension project; write typescript/javascript; compile the extension; install it in VSCode; sometime reload VSCode; debug through the extension host; there's no "just try something" way.
Joyride is a game-changer for VSCode — one can use Clojure-based scripting directly, making it almost Emacs-like, yet still — no advising, can't change editor's core internals — they are not exposed to you, there's no true runtime modification of core behaviors.
So no, VSCode and Neovim do not meet the OP's criteria.
Yes you are. First of all there isn't such a thing as "strict typing", types are either static/dynamic and/or strong/weak. I suppose you meant Elixir has no static types. It is however a strongly typed language.
And just like it usually happens, static typing enthusiasts often miss several key insights when confronting dynamically typed languages like Clojure or Elixir (which was inspired by ideas implemented in Clojure).
It's not simply "white" and "black", just like everything else in the natural world.
You have to address:
- Runtime flexibility vs. compile-time safety trade-offs — like most things, things have a price to it, nothing is free.
- Different error handling philosophies. Sometimes, designing systems that gracefully handle and recover from runtime failures makes far more resilient software.
- Expressiveness benefits. Dynamic typing often enables more concise, polymorphic code.
- Testing culture differences. Dynamic languages often foster stronger testing practices as comprehensive test suites often provide confidence comparable to and even exceeding static type checking.
- Metaprogramming power. Macros and runtime introspection enable powerful abstractions that can be difficult in statically typed languages.
- Gradual typing possibilities. There are things you can do in Clojure spec that are far more difficult to achieve even in systems like Liquid Haskell or other advanced static type systems.
The bottom line: There are only two absolutely guaranteed ways to build bug-free, resilient, maintainable software. Two. And they are not static vs. dynamic typing. Two ways. Thing is - we humans have yet to discover either of those two.
reply