6 ms·
> Maybe I'm just wrong 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
by iLemming 1y ago
> Maybe I'm just wrong
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.
- zdragnar 1y agoYou 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.
- iLemming 1y agoYou 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.
- dmz73 1y agoRuntime 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.
- iLemming 1y agoSure 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.
- sammnaser 1y ago> 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
- iLemming 1y ago> 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.
- sammnaser 1y agoThat comment was specifically engendered by Python trauma
- kstrauser 1y agoNot directly, it wasn’t: >>> open('foo') * 3 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for *: '_io.TextIOWrapper' and 'int' You have to go to some length to get Python to mix types so badly.
- ThinkBeat 1y agoThere is no hard YES or NO, when it comes to typing. It is not a religion. (but it sures feels like it sometimes) There are really good arguments both ways. Just use what you need or go with whatever your current project dictates. Over time you will probably feel drawn to both, for different reasons.
- iLemming 1y ago> 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.
- alternatex 1y agoThat's quite the list of languages. If you're interested in types I would suggest also looking into F#. Shouldn't be tricky for someone who has already experienced C# and is open to different approaches to programming. For me F# was a revelation into what programming language typing can do for the developer.
- Turskarama 1y agoI really like the way F# (and presumably ocaml) do typing, it truly feels like the best of both worlds. Rarely do you need to actually write a type anywhere, and when you do it's because otherwise there might have been a runtime error.
- yxhuvud 1y agoSounds a bit like crystal. Flow typing etc is so powerful.
- spooky_deep 1y agoI wouldn’t say that static types remove the need for unit tests… but they do significantly reduce that need. Static types and unit tests are not equivalent either. A static type check is a proof that the code is constructed in a valid way. Unit tests only verify certain input-output combinations.
- chamomeal 1y agoI think a lot of people feel repulsed by dynamic typing for the same reason I used to: they wrote a lot of JS, and now they write a lot of TS. The experience of working in a JS codebase vs working in a (well-typed and maintained) TS codebase is a wide, wide gulf. I love working in TS, and I absolutely despise working in JS. For a while I extrapolated my experience to mean “static typing is awesome and dynamic typing is horrible”, but then I started learning clojure, and my opinions have changed a lot. There are a ton of things that make a codebase nice to work with. I think static typing raises the quality floor significantly, but it isn’t a requirement. Some other things that contribute to a good experience are - good tests, obviously. Especially property based tests using stuff like test.check - lack of bad tests. At work we have a very well-typed codebase, but the tests are horrible. 100 lines of mocks per 20 lines of logic. They’re brittle and don’t catch any real bugs. - a repl!! - other developers and managers who actually care about quality All four of these examples seem pretty easy to find in the clojure world. Most people don’t learn clojure just to get a job, which is maybe a hidden feature of building your company with a niche language. At the same time, I recognize that most of those examples are “skill issues”. Static typing does a good job of erasing certain skill issues. Which is great, because none of us are perfect all the time!
- iLemming 1y agoExactly this. When speaking about static or dynamic type systems, you can't discuss them in isolation, you need to specify because the holistic experience of types in JS, Python, Clojure, Elixir, Haskell, Rust, etc. will differ, sometimes drastically. Leaning too much to any side is perilous; it may create a false narrative in your head about what types fundamentally mean and how they should be used. Camping too long on either side may create wrong assumptions like that all/most problems are type problems; make you conflate implementation with concepts and make you miss the real tradeoffs; you'd start ignoring context and scale. Static types aren't always about "safety vs. flexibility" - sometimes they're about tooling, refactoring confidence, or documentation. Dynamic types aren't always about "rapid prototyping" - sometimes they enable architectural patterns that are genuinely difficult to express statically. One really needs to see how Rust's ownership system or Haskell's type inference offers completely different experiences, or how Clojure's emphasis on immutability changes the dynamic typing game entirely.