Side-by-side, interactive cheatsheets for Haskell programmers
comparing Haskell to other languages. Every example runs live in your browser — no
setup, no installation.
Choose your own path by reordering languages
A completely different, pragmatic lineage that still lands on familiar ideas. Kotlin descends from Java, not the ML family, yet sealed classes with exhaustive when mirror algebraic data types and nullable types bake "value or absence" into the type system the way Maybe does — while staying strict, OOP-friendly, and mutation-comfortable throughout.
when mirror algebraic data types — same closed, tagged hierarchy, same compiler-checked completeness, for hierarchies (not arbitrary types) onlyString?) rename Maybe but bake it into the type system rather than a wrapper — closer to Swift's Optional than to Haskell's explicit Just/NothingSequence type, and deferred computation needs the explicit by lazy { } delegatevar sit right alongside immutable values, a discipline Haskell enforces structurally that Kotlin only encourages by conventionThe single biggest new concept for a Haskeller: Rust has no garbage collector at all. Maybe/Either map almost exactly onto Option/Result, and type classes map closely onto traits — but Rust's ownership and borrow checker replace Haskell's tracing GC entirely, introducing move semantics and lifetimes, concepts Haskell structurally has no equivalent for.
Rc/Arc reference countingOption/Result are close renames of Maybe/Either — one of the strongest structural parallels on the whole siteimpl Trait for Type mirrors instance Class Type where, default methods includedIterator type rather than an ordinary list? operator on Result/Option is Rust's closest brush with monadic chaining, but it is special-cased at the language level rather than a generalized, user-extensible Monad the way Haskell's do-notation isA completely different lineage that converges on surprisingly similar ideas. Swift is not part of the ML/Haskell family at all, yet enums with associated values mirror algebraic data types, exhaustive switch mirrors pattern matching, and protocol extensions give a real, working type-class analogue — while staying strict, OOP-friendly, and ARC-managed underneath.
switchOptional<T> renames Maybe, but is integrated into the language syntax far more deeply — if let, guard let, and ?? have no single Haskell equivalent constructlazy keywordA rare, genuine kinship despite no shared type system at all. Clojure and Haskell both treat immutability as a core value rather than a syntactic default, and — uniquely among this site's Haskell targets — Clojure's core sequence functions are lazy, the closest parallel to Haskell's own laziness of any target covered. What Clojure lacks entirely is a static type system: not even a typeclass-style workaround exists, only protocols and multimethods for open, runtime dispatch.
(take 5 (range)) mirrors Haskell's take 5 [1..] almost exactly, a rarity among this site's Haskell targets, all of which are otherwise strict->/->>) echo Haskell's own point-free composition style, just read left-to-right instead of right-to-leftThe closest functional language on the .NET runtime — strict where Haskell is lazy. F# shares Haskell's Hindley-Milner inference, automatic currying, and algebraic-data-type-driven design, but trades laziness for eager evaluation and type classes for object-oriented interfaces.
Option and Result are direct renames of Maybe and Either, right down to Some/None and Ok/Errorseq type, and memoization needs an explicit cache rather than falling out of laziness for free|> pipe operator is F#'s idiomatic default, where Haskell code more often composes with . or nests calls instead of using its own & operatorHaskell's closest living cousin. OCaml shares the same ML-family Hindley-Milner inference, algebraic data types, and exhaustive pattern matching — but it is strict by default where Haskell is lazy, and that one difference explains almost everything else: ordinary mutation, real loops, and exceptions-as-the-default all follow directly from OCaml's eager evaluation.
Seq module instead of falling out for freeref, mutable record fields, and genuinely mutable arrays make mutation ordinary and unrestricted, with no IORef/IO wrapper requiredoption's Some/None map directly onto Maybe's Just/Nothing, and result's Ok/Error onto Either's Right/Left — nearly one-to-onerec keyword; a plain let cannot refer to itself the way every Haskell binding automatically can|> pipe operator was born in OCaml — Haskell has no built-in equivalent, only the low-precedence right-to-left $ and the less common &One answer becomes many. Haskell pattern matching and algebraic data types already look a lot like Prolog facts and compound terms, but Prolog's unification runs in any direction and a single query can backtrack through every solution at once, where a Haskell function always returns exactly one value.
= can bind either side and is not limited to destructuring an already-known valuefalse that backtracking can route aroundfindall/3 turns a search into a list — the closest thing to a Haskell list comprehension, except the list did not exist until the backtracking search that produced it finishedOk already feel like Prolog atoms, but atoms carry no type at all — nothing like the compiler stands between an atom and a typoassertz/retract add and remove facts from a live, mutable global database — untracked mutation with no analogue in pure Haskell at all, not even IORefgrandparent/2-style rule runs in any direction from one definition, where a Haskell version commits to one direction of query as soon as it is writtenThe richest functional-programming comparison on the site. Scala's for-comprehensions genuinely generalize across Option, List, and Either the way Haskell's Monad typeclass does, and implicit parameters give real, working ad-hoc polymorphism — but Scala is strict, blends OOP freely with FP, and currying is opt-in rather than automatic.
flatMap/map/withFilter and work uniformly across Option, List, Either, and Future — the closest any language on this site gets to Haskell's generalized Monad abstractionEither[L, R] matches Haskell's Either almost exactly, reusing the names Left/Right with the same failure/success conventionLazyList type, and deferred computation needs the explicit lazy keyworddef add(x: Int)(y: Int)), not automatic the way every Haskell function is curried by defaultvar sit right alongside immutable values, a discipline Haskell enforces structurally that Scala only encourages by conventionThe sharpest contrast a Haskeller will find on this site. Every other Haskell target is still statically typed — Scheme has no type system at all. In exchange it offers two genuinely new capabilities: unhygienic macros that write code at read time, and first-class continuations via call/cc, which Haskell can only model purely through the Cont monad rather than offer as a raw primitive.
define-macro lets ordinary code write new syntax that decides whether and how its own arguments are evaluated — a capability Haskell only approximates with rarely-used Template Haskellcall/cc captures the rest of a computation as a callable value — a raw control-flow primitive Haskell can only model purely, through the Cont monad, never offer directlyeq?/eqv?/equal?) replace Haskell's single, typeclass-dispatched == — picking the wrong one is a real, common Scheme bug with no Haskell equivalentOCaml semantics with JavaScript output, ReScript brings sound type inference, exhaustive pattern matching, and variant types to a syntax that feels familiar to JavaScript developers.
option<T> replaces null and undefined — the type system prevents null dereferences by designswitch, even when you add new variants laterPurely functional, but strict, and with no type classes. Roc shares Haskell's commitment to immutability and effect-tracked purity, but trades laziness for eager evaluation, compile-time reference counting for a generational garbage collector, and Haskell's nominal type classes for structural where-clause constraints.
where [a.method : a -> b] constraint instead of Haskell's nominal, automatic instance resolutiondata/deriving ceremony — structural, anonymous, and inferred, closable or left open with ..Try(ok, err) renames Either, but Roc has no built-in Maybe at all — absence is modeled with an ad-hoc tag union instead! suffix and => arrow rather than the Monad typeclass and do-notation — visible in every signature, but with no single generalized abstraction unifying themwhile loops exist alongside recursion — a genuine surprise for a language this committed to purity and immutability elsewhere