Different functional languages emphasize different aspects of the paradigm. Haskell is pure and lazy by default, with monadic IO and a powerful type system. OCaml is strict by default, supports mutability directly through ref cells and mutable fields, and uses modules rather than type classes as its primary abstraction; both share algebraic data types and pattern matching. F# offers computation expressions, a syntax for defining custom workflows using let! and do! that generalizes monads, async, sequences, and queries, and its Async monad handles asynchronous, parallel, and background work. F#'s unit type, written (), is a type with exactly one value, used as the return type of computations that produce nothing of interest, in contrast to void in C-like languages.
Elixir and F# share the left-to-right pipe operator \( \rvert \) for data flow, where \( x \rvert f(y) \) is \( f(x, y) \), while Haskell's dot operator composes functions right-to-left: pipes are data-focused, whereas composition builds new functions. Clojure builds its entire standard library on persistent data structures — lists, vectors, maps, and sets — that are immutable and use structural sharing for cheap updates, with trie-based vectors giving roughly \( O(\log_{32} n) \) copy costs. Clojure also popularized transducers, composable algorithmic transformations decoupled from the input and output context, so that a single transducer like \( (\text{map inc}) \) can be reused across vectors, lazy sequences, channels, and core.async pipelines. The transduce function composes multiple transformations and applies them during a single fold, avoiding the intermediate collections that ordinary chaining with map and filter would create and dramatically improving performance.
Immutability has a profound effect on concurrency. Because immutable data cannot change, it is automatically thread-safe: no lock is needed to share it, and a whole class of bugs — data races, deadlocks, and stale reads — simply cannot occur. This is the central reason Erlang, Clojure, and Haskell scale to massive concurrency. Erlang's process model takes this further: lightweight isolated processes, each with its own heap and mailbox, communicate solely through message passing, with crash isolation built in. The let it crash philosophy holds that defensive error handling often hides bugs; instead, processes are allowed to fail and supervisors restart them, making failure observable, fast, and self-healing — aligning with FP's preference for isolating side effects. For cases that genuinely require shared mutable state, software transactional memory provides atomic, isolated access without explicit locks: transactions read and write to TVars or refs, and on conflict the runtime retries automatically, with the composable property that any function can run inside a transaction.