Get AI summaries of any video or article — Sign up free
2 Language Creators vs 2 Idiots | The Standup thumbnail

2 Language Creators vs 2 Idiots | The Standup

The PrimeTime·
6 min read

Based on The PrimeTime's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Functional programming’s mainstream impact came largely through individual ideas (like lambdas, streams, and type inference) rather than through adopting “functional programming” as a unified identity.

Briefing

Functional programming didn’t “take off” in the way many early advocates expected—not because the ideas failed, but because the label became slippery and the mainstream world absorbed the useful parts without adopting the whole package. Jose Valim, creator of Elixir, frames functional programming as a bundle of ideas that later spread into mainstream languages (streams, lambdas, type inference), while other aspects never became universal. He also argues that newer languages often avoid the “functional vs object-oriented” branding entirely, preferring more specific categories like systems programming—suggesting that Elixir may not need to market itself primarily through the functional label in 2025.

Gingerbill, creator of Odin, pushes the critique further by attacking the term itself. “Functional programming” can mean everything from pure, side-effect-free code to broader styles like treating functions as first-class values and using closures. Under that joke definition, functional programming “never caught on” because pressing a button is a side effect. More seriously, Gingerbill says many people encounter functional languages as research projects—especially when they come with heavy complexity—making them hard to read and unattractive for everyday mutation-heavy software. Still, he points to concrete functional-adjacent features that show up everywhere: closures and immutability.

The conversation then pivots to why Erlang-style concurrency feels like a better fit than many “functional concurrency” attempts. Gingerbill calls Erlang his favorite language and describes its concurrency model as CSP-like: lightweight processes communicate by sending messages, and immutability by default helps fault tolerance by preventing shared-state corruption. He contrasts this with approaches that try to manage concurrency differently, which he says he doesn’t like as much.

From there, the debate expands into how language design trade-offs shape adoption. Jose compares immutability’s benefits—clarity and easier reasoning under concurrency—to the “banana/gorilla” problem of object-oriented code that can reach far into shared state. He also notes that Rust can deliver some immutability benefits via its type system even if it isn’t marketed as functional.

Gingerbill’s Rust rant is less about theory and more about ergonomics and ceremony. He characterizes Rust as “ML in disguise pretending to be a C++,” emphasizing ownership, lifetimes, pattern matching, and macro-heavy syntax. His complaint is that the language’s semantic machinery forces developers into constant friction—especially lifetime management—rather than letting them express intent directly. He also argues that macros often signal missing language features, and that debugging meta-programming is inherently harder.

Macros become a flashpoint with Jose defending Elixir’s approach. He says Elixir keeps macros limited in scope: Phoenix’s router is macro-heavy at the entry point, but controllers, templates, and LiveViews are regular code. Jose also defends macros as part of a “small extensible language” design philosophy—minimizing “cheating” by keeping the language’s core constructs under the language author’s control.

Finally, the group lands on tooling and ecosystem mechanics: LSPs are expensive to implement because they require deep compiler integration, and package managers can accelerate “dependency hell” by automating trust without verification. Jose and Gingerbill both argue that language ecosystems should encourage fewer, higher-quality dependencies and that developers should read dependency source code before adopting it.

Across the discussion, the central theme is practical: adoption depends less on labels and more on how language features interact with real-world debugging, concurrency, ergonomics, and dependency management.

Cornell Notes

Functional programming’s failure to “take off” is traced to two problems: the label is vague, and the mainstream world absorbed useful functional ideas without adopting functional programming as a unified identity. Jose Valim argues that many functional concepts—like lambdas, streams, and type inference—became mainstream, while newer languages increasingly avoid the functional/object-oriented branding. Gingerbill adds that many functional languages feel like research projects, and that “functional” can mean everything from purity to first-class functions and closures, making the term hard to market or apply consistently. The discussion also links design choices to adoption: Erlang’s immutable, message-passing concurrency supports fault tolerance, while Rust’s ownership/lifetimes and macro ecosystem create friction for some developers. Tooling and ecosystem design—debuggability, LSP integration cost, and dependency automation—shape whether languages thrive in practice.

Why does “functional programming” fail as a marketing label, even when functional ideas succeed?

Jose Valim treats functional programming as a collection of ideas that later became mainstream in many languages (e.g., streams, lambdas, and type inference). Gingerbill adds that “functional programming” can mean purity (no side effects) or broader concepts like declarative style and first-class functions/closures. Under the purity definition, real programs immediately violate the rules because user interaction and I/O are side effects. That mismatch makes the label both technically slippery and practically misleading, even when individual functional techniques become widely adopted.

What does Erlang’s concurrency model have to do with functional programming’s appeal?

Gingerbill describes Erlang’s concurrency as CSP-like: many lightweight processes run concurrently and communicate by sending messages (analogous to Go routines plus channels, and Elixir’s send/receive). Immutability by default prevents shared-state corruption, which matters for fault tolerance—if one process fails while mutating shared memory, it can pollute the system. By avoiding shared mutable state, Erlang’s design makes resilience easier to reason about.

How do immutability and clarity connect in Jose Valim’s view of language design?

Jose argues immutability improves code comprehension: if a called function cannot change data elsewhere in memory, the inputs and outputs define behavior more cleanly. He contrasts this with object-oriented “banana/gorilla” reachability, where calling an object can indirectly access and mutate a much larger shared environment. He also ties immutability to concurrency: data races largely disappear when concurrent code doesn’t mutate the same memory locations.

Why does Gingerbill dislike Rust, beyond personal preference?

His critique is ergonomic and systemic. He characterizes Rust as carrying heavy semantic machinery—ownership and lifetime systems—along with pattern matching and macro usage. He argues developers spend too much time fighting lifetimes and type-level ceremony instead of expressing intent. He also claims Rust’s macro approach (e.g., printing via macros) adds complexity and makes debugging harder, especially when meta-programming expands into code that tools can’t easily step through.

What’s the “no cheating rule” in Elixir’s macro philosophy?

Jose Valim frames Elixir as a small extensible language where language constructs are meant to be extended by users through the language’s own extension mechanism. He says he avoids adding constructs “just for the language author,” because that would undermine the extensibility model. He also argues macros don’t meaningfully slow compilation in practice and that Elixir’s macro usage is constrained—Phoenix’s router is macro-heavy at the entry point, but application logic remains regular code.

Why do the guests criticize package managers and LSPs as ecosystem accelerants of pain?

Gingerbill distinguishes packages, repositories, and build systems, then argues package managers automate dependency hell by rapidly pulling in large dependency graphs without meaningful verification. He points to how many ecosystems lack a well-defined “package” concept in the language, forcing extra conventions and tools. For LSPs, the cost is deep compiler integration; Gingerbill says implementing an LSP for Odin would require rewriting the compiler so it can serve as a library, which is a major engineering commitment.

Review Questions

  1. What specific reasons do Jose Valim and Gingerbill give for why the term “functional programming” doesn’t map cleanly to how people actually build software?
  2. How does Erlang’s message-passing plus immutability reduce fault-tolerance risks compared with shared mutable state?
  3. What trade-offs do the guests associate with macros and with package managers, and how do those trade-offs affect debugging and long-term maintenance?

Key Points

  1. 1

    Functional programming’s mainstream impact came largely through individual ideas (like lambdas, streams, and type inference) rather than through adopting “functional programming” as a unified identity.

  2. 2

    The term “functional programming” is technically ambiguous, ranging from purity/no side effects to broader styles like first-class functions and closures.

  3. 3

    Erlang’s CSP-like concurrency and immutability by default are presented as practical tools for fault tolerance by avoiding shared-state corruption.

  4. 4

    Rust is criticized for ergonomic friction: ownership and lifetime semantics plus macro-heavy syntax can shift developer effort from intent to type-level ceremony.

  5. 5

    Elixir’s macro system is defended as part of a “small extensible language” design, with macros concentrated at key entry points (e.g., Phoenix routing) rather than pervasively in application logic.

  6. 6

    Language tooling and ecosystem mechanics matter: LSPs require deep compiler integration, and package managers can accelerate dependency hell by automating trust and dependency graphs.

  7. 7

    Adoption depends on debuggability and maintainability—especially for meta-programming—so language design must account for how developers inspect and troubleshoot code.

Highlights

Functional programming didn’t fail so much as get absorbed: mainstream languages adopted many functional ideas while avoiding the branding.
Erlang’s concurrency is framed as CSP-style message passing with immutability, making fault tolerance easier by preventing shared-state pollution.
Rust is criticized as “ML in disguise,” with ownership/lifetimes and macros creating ceremony and debugging pain.
Elixir’s macros are defended as a deliberate extensibility mechanism, with Phoenix’s router as the main macro-heavy entry point.
Package managers are attacked as automation that can speed up dependency hell by pulling in large graphs without verification.

Topics

Mentioned