Fact-checked by Grok 2 weeks ago

PureScript

PureScript is a small, strongly typed, that compiles to readable , enabling the use of advanced techniques within the JavaScript ecosystem. Designed by Phil Freeman and first released in late 2013, it is inspired by and aims to provide a robust environment for large-scale, disciplined in and beyond. The language features a rich including algebraic data types, , higher-kinded types, row polymorphism for extensible , type classes with functional dependencies, and higher-rank polymorphism, all of which support expressive and safe code construction. PureScript allows seamless interoperability with existing libraries by providing type annotations for foreign functions, facilitating its use in browser-based applications, server-side code via , and other JavaScript environments. Its compilation process produces efficient, human-readable output, and the ecosystem includes tools like the build system, Pursuit package database, and extensive community libraries for domains such as web UIs, data processing, and backend services. While primarily targeting , experimental backends like PureScript Native support compilation to and Go for broader applicability. PureScript maintains an active open-source community, with ongoing development hosted on GitHub and resources like the official "PureScript by Example" book to aid learning and adoption. As of November 2025, the latest stable release is version 0.15.15, featuring enhanced command-line options for better tooling integration and continued focus on type safety and developer productivity. Recent updates include the adoption of statically-linked binaries for Linux releases since June 2025 to simplify distribution.

Introduction

Overview

PureScript is a strongly typed, purely functional programming language designed for compiling to efficient and readable code in various target environments. It primarily transpiles to JavaScript for web development, with experimental and community backends supporting C++11 (via PureScript Native), Erlang (via Purerl), and Go (via PureScript Native) for other platforms, enabling Haskell-inspired programming paradigms outside traditional Haskell ecosystems. The language's primary goal is to facilitate the development of robust, type-safe applications, particularly for frontend web applications and server-side logic, by producing optimized output that integrates seamlessly with existing ecosystems like the JavaScript runtime. Developed by Phil Freeman, PureScript was first released in 2013 as an open-source project. It is licensed under the BSD 3-clause license, promoting broad adoption and contributions from the developer community. The project is hosted on , where it has fostered a collaborative environment for enhancements and maintenance. As of November 2025, PureScript remains an active, community-driven initiative, with the latest stable release being version 0.15.15, released in February 2024. This ongoing development supports its use in production environments, backed by tools like the Pursuit package search and a vibrant forum for users and contributors.

Design Philosophy

PureScript's design philosophy draws significant inspiration from , adopting core principles such as pure functions, which prohibit side effects in function definitions to ensure , and immutability, where data structures cannot be modified after creation to promote safer and more predictable code. Unlike , however, PureScript opts for strict evaluation by default rather than , prioritizing predictability and performance alignment with its primary compilation target, . This choice facilitates easier debugging and avoids the complexities associated with , such as space leaks, while still allowing optional laziness through libraries like Data.Lazy when needed. Central to PureScript's philosophy is type-driven development, where an expressive type system—featuring algebraic data types, higher-kinded types, and type classes—enables developers to catch errors at compile time and use types as a guide for implementation. This approach shifts much of the burden of correctness from runtime checks to the compiler, fostering robust software through techniques like property-based testing and equational reasoning enabled by purity. The language's strong, static typing ensures that type inference is powerful yet decidable, supporting refactoring and abstraction without sacrificing usability. Interoperability with host environments, particularly , is a foundational goal, achieved through direct compilation to readable and efficient code that incurs no runtime overhead from the . PureScript allows seamless import of JavaScript modules with typed foreign function interfaces, enabling developers to leverage existing ecosystems like packages while writing pure functional code for critical logic. This design supports mixed-language projects, where PureScript handles type-safe computations and JavaScript manages imperative or performance-critical sections. To balance strict typing and purity—which enable aggressive optimizations and reliable code—the language incorporates flexible features like row polymorphism for extensible records, allowing variant-like behavior without sacrificing . These trade-offs prioritize long-term maintainability and efficiency over immediate expressiveness, aiming to produce output code that is not only performant but also comprehensible to developers, thereby facilitating adoption in real-world applications across , and mobile contexts.

History

Origins and Development

PureScript was created in 2013 by Phil Freeman, motivated by the shortcomings of existing Haskell-to- compilers like GHCJS and UHC, which suffered from large runtime overheads, complex build processes, and suboptimal performance for web applications. While working on rewriting a application in , Freeman sought a stronger and more efficient compilation pipeline tailored for environments. The language's initial goals centered on producing a , strictly evaluated alternative inspired by , emphasizing clean, readable output without Haskell's or extensive runtime dependencies. This design aimed to enable expressive with advanced type features, such as row polymorphism and type classes, while ensuring seamless interoperability with via straightforward foreign function interfaces (FFI). Freeman implemented the compiler in itself, using relatively simple language constructs to make it accessible for potential contributors. Shortly after its inception in October 2013, the project transitioned to open-source under the Apache 2.0 license on , rapidly attracting community interest and contributions from developers interested in for the web. An active IRC channel on further supported early collaboration, leading to the development of supporting libraries and tools. Among the key early milestones was the first public release toward the end of 2013, which included core features like the and FFI support. Soon after, tools such as were introduced to streamline building and managing PureScript projects, integrating with package managers like Bower for dependency handling. remained the primary architect through the initial years, guiding the language's evolution until the community increasingly took over maintenance responsibilities.

Major Releases and Milestones

PureScript's first public release occurred in late , marking the language's early availability as a Haskell-inspired targeting . In 2015, version 0.7 introduced row polymorphism, enabling more flexible record types and structural typing for better expressiveness in data handling. Version 0.13 followed in 2019, featuring internal parser and grammar enhancements that improved overall stability, including refinements to the (FFI) for smoother interoperability. The 0.15 series began with version 0.15.0 in April 2022, adding support for ES modules to enable modern bundling and tree-shaking, alongside a modernized FFI that simplified foreign code integration. Minor releases like 0.13.8 in and 0.15.14 in 2024 addressed performance challenges through optimizations like reduced memory usage during compilation and faster . As of June 2025, future releases after v0.15.15 incorporate statically-linked binaries to facilitate easier distribution across platforms without dependency issues. Key milestones include the introduction of Pursuit in 2014, a searchable documentation tool that centralized API exploration for packages and replaced fragmented local docs. The Spago package manager launched in December 2018, providing a Dhall-based build system that streamlined dependency management and project setup. In September 2025, the PureScript Analyzer entered public alpha, offering enhanced IDE support through a dedicated language server for better code analysis and autocompletion. The project adopted semantic versioning guidelines to manage breaking changes, ensuring compatibility in core libraries and compiler updates. Later releases focused on reducing output bundle sizes via ES module compatibility, aiding deployment efficiency.

Language Fundamentals

Syntax and Semantics

PureScript employs a syntax closely resembling that of , facilitating familiarity for developers experienced in languages. The language is indentation-sensitive, where significant whitespace determines the scope of blocks such as function bodies, case expressions, and module declarations, eliminating the need for explicit braces or keywords like "begin" and "end." For instance, a simple function definition might appear as:
double :: Int -> Int
double x = x * 2
Here, the body x * 2 is indented relative to the function name to denote its . Data types are declared using the data keyword, supporting algebraic data types with constructors, such as data Maybe a = [Nothing](/page/Nothing) | Just a, which mirrors Haskell's approach to defining sum types. Function definitions leverage for destructuring arguments, allowing concise handling of different cases without additional boilerplate. The system in PureScript is hierarchical, organizing code into like Data.List to promote modularity and avoid global namespace pollution. are declared with module ModuleName (export1, export2) where, specifying explicit exports to control visibility, while imports can be unqualified, qualified, or hiding specific names to prevent clashes. Qualified imports, such as import Data.Maybe as Maybe, prefix identifiers with the alias (e.g., Maybe.Just), enabling safe usage of multiple with overlapping names. This structure supports large-scale code organization, with the enforcing boundaries during compilation. Basic constructs include in function definitions or case expressions, guards for conditional branching using |, and do-notation for sequencing operations, particularly useful in monadic contexts. allows exhaustive checks, as in:
head :: List a -> Maybe a
head [Nil](/page/Nil) = [Nothing](/page/Nothing)
head ([Cons](/page/Cons) x _) = Just x
Guards provide an alternative to , evaluated from top to bottom, for example:
bmiTell :: Number -> Number -> String
bmiTell weight height
  | weight / sq height <= 18.5 = "Underweight"
  | weight / sq height <= 25.0 = "Normal"
  | otherwise = "Overweight"
Do-notation desugars to bind operations, offering a imperative-like syntax for composing computations, such as do x <- action1; y <- action2; pure (x + y). PureScript adopts strict evaluation semantics, where function arguments are fully evaluated before application, contrasting with Haskell's lazy evaluation and ensuring predictable runtime performance without unexpected thunk buildup. This call-by-value strategy means expressions like f (g ()) first compute g () completely before passing the result to f, aiding in performance tuning for JavaScript targets where laziness must be explicitly implemented via thunks if needed. By default, PureScript enforces immutability in its core language, treating all bindings as immutable value assignments rather than variables that can be reassigned; there are no mutable references in pure code, and data structures are persistent, with operations returning new instances rather than modifying existing ones. For example, updating a record creates a new record: newBook = book { published = 2014 }, preserving the original unchanged, which aligns with the language's pure functional paradigm and prevents side effects in non-effectful code.

Type System

PureScript employs a strong, static type system that ensures type safety at compile time, with no types present at runtime to maintain performance when targeting JavaScript. The system features full type inference, allowing the compiler to deduce types for expressions without explicit annotations in many cases, though programmers can provide type signatures for clarity, documentation, or to resolve ambiguities. This inference is powered by Hindley-Milner-style algorithms, enabling concise code while catching errors early. A core element of the type system is support for algebraic data types (ADTs), which facilitate precise data modeling through sum types and product types. Sum types represent choices among variants, such as the Maybe a type with constructors Nothing and Just a for handling optional values, or Either e a for encapsulating success or error cases. Product types combine multiple values, as in records or tuples, allowing structured data representation. These ADTs promote composability and exhaustiveness checking via pattern matching, reducing runtime errors. Type classes provide ad-hoc polymorphism by defining interfaces with associated functions, constrained to specific types via instances. For example, the Eq a class includes an eq :: a -> a -> Boolean method for equality, with instances for primitives like Boolean and String, enabling polymorphic functions like generic comparison. Similarly, Show a supports string conversion with show :: a -> String. Instances can be derived automatically for ADTs, enhancing code reuse without sacrificing type safety. Row polymorphism extends the to handle extensible and variants, using row variables to represent unknown additional fields. A might accept { name :: String | r } where r is a polymorphic row, allowing inputs with extra fields like { name :: String, age :: Int | r } without type errors, provided required fields are present. This feature supports open , contrasting with closed structural typing in other languages, and is crucial for libraries dealing with dynamic data. PureScript's type system includes kinded types, where kinds classify types (e.g., * for concrete types like Int, # Type for rows), enabling higher-kinded types for abstraction over type constructors. Higher-kinded types allow type classes like Functor f with map :: (a -> b) -> f a -> f b, where f has kind Type -> Type, supporting patterns such as functors and applicatives for container-like structures. This facilitates type-level programming, where types themselves are manipulated, drawing from Haskell's System Fω extensions. Typed holes support type-driven development by allowing placeholders like ?hole in code, prompting the to report the expected type and suggest fitting values or functions from the environment during compilation errors. This interactive feedback aids exploration, such as inferring a String -> Int -> String implementation, and integrates with editors for without full type annotations.

Core Features

Functional Programming Constructs

PureScript emphasizes pure functions as the cornerstone of its functional programming paradigm, ensuring that computations produce the same output for the same input without observable side effects, which facilitates reasoning about code and enables optimizations like memoization. Functions are defined with a type signature specifying input and output types, such as add :: Int -> Int -> Int, and implemented using the equals sign followed by the body, promoting referential transparency. Higher-order functions like map, filter, and fold enable composition of pure transformations over data; for instance, map applies a function to each element of an array, returning a new array without modifying the original, as in map (\n -> n * 2) [1, 3, 5] yielding [2, 6, 10]. All functions in PureScript are curried by default, meaning a function declared to take multiple arguments, like subtract :: [Int](/page/INT) -> [Int](/page/INT) -> [Int](/page/INT), actually takes one argument and returns a new of type [Int](/page/INT) -> [Int](/page/INT), allowing seamless partial application. Partial application creates specialized functions from general ones, such as defining subtractTen :: [Int](/page/INT) -> [Int](/page/INT) as subtract 10, which subtracts 10 from its input. This currying supports point-free programming styles, where functions are composed without explicitly naming arguments, enhancing conciseness and modularity in pure computations. Lambda expressions, or anonymous functions, provide a way to define short, inline functions using the backslash syntax, such as \x -> x + 1, which can be passed directly to higher-order functions like filter (\n -> n > 0) [-1, 0, 1] to retain positive numbers in a new array [1]. Function composition is facilitated by the <<< operator from the Semigroupoid type class, which chains functions such that (f <<< g) x evaluates as f (g x); for example, length <<< trim first trims whitespace from a string and then computes its length, both purely. These constructs allow building complex pipelines of pure transformations, such as foldl (+) 0 <<< map (\x -> x * x) <<< filter even to sum the squares of even numbers in an array. Recursion serves as the primary mechanism for iteration and control flow in PureScript, replacing imperative loops with functional definitions that process data immutably. Functions like factorial :: Int -> Int are defined recursively as factorial 0 = 1 and factorial n = n * factorial (n - 1), with base cases preventing infinite computation. Pattern matching on algebraic data types (ADTs) destructures inputs for recursive cases, as in a List ADT defined by data List a = Nil | Cons a (List a), where sum :: List Int -> Int matches Nil -> 0 or Cons x xs -> x + sum xs. Guards provide conditional branching in recursive functions using boolean expressions, such as in gcd :: Int -> Int -> Int with gcd n m | n == 0 = m | otherwise = gcd (m mod n) n, ensuring tail-recursive optimization where possible. Immutable data structures underpin PureScript's pure approach, with records serving as lightweight, named collections like { firstName :: String, lastName :: String }, accessed immutably via dot notation without alteration. Strict linked lists, modeled after Haskell's cons cells, support efficient pure operations like cons (:) to prepend elements, creating new lists as in Cons "Alice" Nil, while arrays from the Data.Array module provide indexed, immutable access for JavaScript interoperability, with functions like foldl accumulating results into new values. In pure contexts, error handling relies on sum types like Either e a from the purescript-either package, which encodes success (Right a) or failure (Left e) explicitly, avoiding exceptions. For example, a function might return Either String Number with Right (x / y) on success or Left "Division by zero" otherwise, allowing to propagate or handle errors purely, such as case divide 10 0 of Left msg -> "Error: " <> msg; Right result -> show result. This approach ensures and in functional pipelines.

Effect System and Monads

PureScript enforces purity by default, ensuring that functions without explicit effect annotations cannot perform side effects such as I/O operations or mutable state modifications. Side effects are explicitly isolated within the Effect monad, which represents computations that interact with the external world, such as console output or , while maintaining and generating efficient code. This approach prevents unintended impurities and allows pure functions to be composed freely without risking hidden effects. Prior to PureScript version 0.12, side effects were managed via the monad, typed as Eff r a, where r was a row type specifying the effects involved, such as { console :: CONSOLE | r } for console interactions, enabling modular tracking of impurities at the type level. In recent versions, the monad (Effect a) replaced Eff, simplifying the system by removing row-based effect tracking, as the original rows proved insufficient for reliably guaranteeing effect composition without excessive complexity. Despite this change, row types remain integral to PureScript's for records and polymorphism, and some libraries use row constraints to model effectful behaviors indirectly. The Effect monad supports standard monadic operations, including do-notation as syntactic sugar for sequencing effectful computations via bind (>>=). For instance, generating and logging a random number can be written as:
purescript
import Effect (Effect)
import Effect.Console (logShow)
import Effect.Random (random)

main :: Effect Unit
main = do
  n <- random
  logShow n
This desugars to random >>= logShow, ensuring sequential execution while optimizing away unnecessary closures in the compiled JavaScript. Common monads in PureScript include Maybe for handling optional values and Either for error propagation, both operating in the pure realm, alongside Effect for synchronous external interactions. For asynchronous operations like promises or callbacks, PureScript provides the Aff monad, which wraps non-blocking effects and supports cancellation via fibers, preventing callback hell while integrating seamlessly with Effect through monad transformers. Monad transformers enable stacking multiple effects, such as combining State for mutable state with Effect for I/O using StateT s Effect a, or Reader for configuration with Writer for logging via ReaderT r (WriterT w Effect) a. This layered approach, exemplified in domain-specific stacks like RWS (Reader-Writer-State) over Effect, allows complex effect compositions while preserving outside the base monad. For safe local mutation, the ST monad uses row-polymorphic references (STRef r a) to confine changes to a scoped region, avoiding global effects.

Compilation and Targets

Compiler Architecture

The PureScript compiler, known as purs, is implemented in and processes source code through a series of stages to produce optimized output. The pipeline begins with lexing, where the source code is tokenized into lexical elements such as identifiers, keywords, and symbols. This is followed by , which constructs a concrete syntax tree (CST) using a grammar inspired by Haskell's layout-sensitive syntax, handling features like indentation-based blocks and guards. The CST is then transformed into an (AST), stripping away concrete syntax details. Subsequent stages include renaming, which resolves module imports, qualified names, and local bindings to ensure unambiguous references across the codebase. Desugaring then simplifies the by expanding high-level constructs, such as do-notation for monadic computations, case expressions, and updates, into a more primitive form suitable for further . Type checking occurs next, employing bidirectional inference with constraint solving to validate types, infer polymorphic types, and resolve instances; this involves generating constraints from unification problems, applying skolemization for , and performing AST rewrites to handle higher-rank polymorphism and row polymorphism without relying on ordered contexts. The checked is desugared further into CoreFn, a small, functional based on F-omega with extensions for effects and records, facilitating subsequent transformations. From CoreFn, the applies basic optimizations before JavaScript emission, with advanced optimizations available via external tools like purescript-backend-optimizer. Finally, emission generates readable, modular code from the optimized CoreFn, preserving functional idioms where possible. The supports incremental compilation by tracking module dependencies and rebuilding only affected modules upon changes, improving development iteration speed in large projects. For developer tooling, the compiler integrates with editors like VS Code and Vim through the PureScript Language Server Protocol (LSP) implementation, enabling real-time type checking, error reporting, and without full recompilation. As an open-source hosted on , contributions to the compiler occur via pull requests, with the Haskell codebase allowing for potential self-hosting experiments by compiling equivalent PureScript modules.

Output Formats and Interoperability

PureScript primarily compiles to , generating readable code in ES modules format ( 2015 and later), which facilitates debugging and integration into modern web projects. The output emphasizes minimal overhead, producing self-contained modules without requiring a dedicated runtime for the core language features. This allows PureScript applications to bundle efficiently using tools like , maintaining compatibility with standard JavaScript ecosystems. Beyond JavaScript, experimental backends enable compilation to other languages for specialized use cases. The purescript-native project supports C++11 as a target for native performance in systems-level applications and Go for concurrent systems programming. Similarly, the purerl backend targets Erlang, leveraging its strengths in concurrency and fault tolerance for distributed systems. As of 2025, these backends remain experimental, focusing on full interoperability while extending PureScript's applicability beyond the web. Interoperability with host languages occurs through PureScript's Foreign Function Interface (FFI), which uses foreign import and foreign export declarations to bridge code boundaries. For , developers define type signatures in PureScript modules (e.g., .purs files) and implement the corresponding logic in adjacent JavaScript modules (e.g., .js files), enabling direct calls like foreign import encodeURIComponent :: [String](/page/String) -> [String](/page/String). The compiler enforces across this interface by requiring PureScript types to align with JavaScript runtime representations, such as wrapping JavaScript objects as records or handling null values with Maybe types to prevent mismatches. This type-safe FFI supports practical interop patterns, including gradual integration into projects. For instance, PureScript can wrap components via bindings in the purescript-react package, allowing developers to define PureScript functions that render elements while exporting them for use in code. Such patterns enable hybrid applications where PureScript handles complex logic and state management, interoperating seamlessly with existing libraries through uncurried functions (FnN types) or asynchronous promises via the Aff .

Libraries and Tools

Standard Library

The Standard Library of PureScript comprises a set of core packages maintained by the official PureScript organization, providing essential types, functions, and abstractions for everyday programming needs. These packages form the foundation for most PureScript projects and are designed to be lightweight, emphasizing functional programming principles while avoiding dependencies on external JavaScript libraries. The Prelude module, imported by default in every PureScript file, serves as the entry point to the standard library and re-exports foundational elements from other core packages. It defines basic types such as Int for integers, String for text, Boolean for truth values, and Number for floating-point numbers, along with fundamental functions like id (identity), const (constant function), and flip (argument reversal). Common operators for equality (==), comparison (compare), and composition (>>>) are also included, enabling concise expression of simple computations. Additionally, the Prelude exposes key type classes such as Eq for equality, Ord for ordering, Semigroup and Monoid for combining values, and higher abstractions like Functor, Apply, Applicative, Bind, and Monad to support composable code patterns. Data manipulation is handled by dedicated modules for common structures. The Data.Array module provides comprehensive functions for working with JavaScript arrays, including map and filter for transformations, foldl and foldr for reductions, concat for joining, and sortBy for ordering, all while maintaining type safety and efficiency. The Data.Maybe module introduces the Maybe type (Nothing or Just a) for representing optional values, with utilities like maybe (defaulting), fromMaybe (unwrapping), and isJust/isNothing for checks. Similarly, Data.Either defines the Either sum type (Left e for errors or Right a for success), offering functions such as either (pattern matching), hush (extracting success), and note (converting to Maybe). The Data.Tuple module supports pairs with constructors like Tuple and accessors fst and snd, facilitating simple product types without records. These modules emphasize immutability and pure functions, aligning with PureScript's functional paradigm. Control abstractions are centralized in the package, which builds on the Prelude to enable advanced composition. The Control.Applicative module extends applicative functors with pure for injecting values and <*> for applying functions in contexts like Maybe or arrays. The Control.Monad and Control.Bind modules define monadic binding via >>= (bind) and =<< (bind flipped), supporting sequencing of computations with effects abstracted away. Features like ApplicativeDo notation allow do-style syntax for applicatives, improving readability for non-monadic code, as in:
purescript
do
  x <- Just 1
  y <- Just 2
  pure $ x + y
This evaluates to Just 3, demonstrating applicative lifting without full monadic structure. For operations that cannot be total, the standard library includes language-specific modules with explicit warnings. The Partial module defines the Partial type class and functions like fromJust (unsafe Maybe unwrap) and partial folds, allowing developers to mark code that assumes certain inputs exist but requiring explicit imports to signal potential incompleteness. Unsafe operations are segregated in the Unsafe package, such as unsafeCoerce for bypassing the type system and unsafeReference for low-level memory access, both strongly discouraged in favor of safer alternatives to prevent runtime errors. These modules promote disciplined use, with compiler pragmas often flagging partial patterns. Basic mathematics and utilities round out the essentials. The Math module supplies arithmetic functions like abs, ceil, log, and trigonometric operations (sin, cos, tan), along with constants such as pi and e. Ordering is handled via Data.Ordering with LT, EQ, and GT constructors, integrated into Ord instances for comparable types. Unlike more comprehensive libraries in other languages, PureScript's standard library remains focused and minimal, deferring advanced numerics, strings, or dates to the broader ecosystem while ensuring core utilities are performant and pure.

Build Tools and Package Management

Spago serves as the primary package manager and build tool for PureScript projects, introduced in 2018 as a modern alternative to earlier tools like and . It facilitates dependency installation, project building, and integration with documentation platforms by leveraging curated package sets defined in configurations to ensure compatibility. Spago supports both single-package setups and monorepos, allowing developers to manage multiple related packages within a single repository while handling version pinning and reproducible builds. The PureScript compiler, invoked as purs (formerly psc), forms the core build tool, compiling source code to JavaScript or other targets with options for optimization and customization. Key flags include --codegen corefn for intermediate representations, --optimizations to enable performance enhancements like dead code elimination, and --target to specify output formats such as ES modules. Spago typically wraps these invocations, streamlining commands like spago build to compile modules while respecting dependency graphs. For testing, PureScript projects commonly use the purescript-spec framework, which provides a declarative DSL for defining synchronous and asynchronous unit tests inspired by Haskell's HSpec. It supports running tests via runners like purescript-spec-node for Node.js environments or purescript-spec-mocha for browser-based execution. Additionally, interoperability with JavaScript testing tools is available through packages like purescript-jest, enabling PureScript tests to integrate with Jest for broader ecosystem compatibility and parallel execution. Documentation generation and discovery rely on Pursuit, a searchable database of auto-generated API docs for PureScript packages and modules. Pursuit extracts type signatures, function documentation, and examples directly from source code annotations, allowing queries by name or approximate types to facilitate library exploration. Spago integrates this by offering spago docs to publish project documentation to Pursuit, ensuring up-to-date references for contributors. Development workflows benefit from editor integrations that provide real-time type checking via the PureScript language server, available as plugins for environments like VS Code and Vim. These tools offer features such as inline error reporting, hover information for types, and autocomplete based on project dependencies. In 2025, the PureScript Analyzer—a Rust-based compiler frontend—emerged as an advanced tool for deeper static analysis, including enhanced diagnostics and refactoring support beyond the standard language server. Best practices for PureScript projects emphasize using Spago for monorepo structures to centralize dependency management across packages, reducing duplication and improving consistency. For continuous integration, GitHub Actions workflows leverage the setup-purescript action to install the toolchain, run builds, and execute tests efficiently, often caching Spago's package outputs to minimize repetition across runs.

Examples

Basic Programs

PureScript programs are structured as modules, which declare their name, imports, and exports. A minimal module imports the necessary libraries, defines functions or values, and typically includes a main entry point for executable code. The Prelude, a standard library providing common functions and operators, is often imported implicitly or explicitly to simplify basic operations. Programs compile to JavaScript, enabling execution in Node.js or browser environments. A simple "Hello World" program demonstrates the use of the Effect monad for side effects, such as console output. The following example defines a main function that logs a string to the console:
purescript
module Main where

import Effect.Console (log)

main :: Effect Unit
main = log "Hello, World!"
Here, the module is named Main, importing the log function from Effect.Console. The main function has an explicit type signature Effect Unit, indicating it performs an effect but returns no meaningful value; PureScript's type inference could derive this type without the annotation. To illustrate type inference, consider a basic addition function. Without an explicit signature, the compiler infers add :: Int -> Int -> Int from the use of the + operator on integers:
purescript
add x y = x + y
Adding an explicit signature clarifies intent and aids readability, especially in larger codebases:
purescript
add :: Int -> Int -> Int
add x y = x + y
A curried variant, addFive, infers Int -> Int from partial application:
purescript
addFive = add 5
This showcases how inference reduces boilerplate while maintaining type safety. For handling potential errors like , PureScript uses the Maybe type to represent optional values, avoiding runtime exceptions. A safe division function employs to return Nothing for invalid cases or Just with the result otherwise:
purescript
import Prelude
import Data.Maybe (Maybe(..))

safeDivide :: Int -> Int -> Maybe Int
safeDivide _ 0 = Nothing
safeDivide a b = Just (a / b)
To use this, extracts the value safely:
purescript
result :: Maybe Int
result = safeDivide 10 2

showResult :: String
showResult = case result of
  Nothing -> "Division error"
  Just x  -> "Result: " <> show x
This prints "Result: 5", demonstrating how Maybe and promote robust data handling. To compile and run these programs, use , the standard build tool and . Initialize a project with spago init, which sets up a src/Main.purs file. Build with spago build, generating in the output/ directory. Execute via spago run, which compiles and invokes the main function in , or manually with node output/Main/index.js. This workflow integrates compilation to and runtime execution seamlessly.

Advanced Usage Patterns

Advanced usage in PureScript often involves integrating multiple language features to handle real-world scenarios, such as asynchronous operations, dynamic data structures, stateful computations, foreign interactions, and performance-critical algorithms. These patterns leverage the for safety while composing effects and abstractions effectively. One common pattern is asynchronous data fetching using the Aff monad for non-blocking effects, combined with error handling via Either. The purescript-affjax library facilitates HTTP requests within Aff, returning responses wrapped in Either to distinguish success from failure. For instance, a function to retrieve data from a URL might look like this:
purescript
import Affjax.ResponseFormat as ResponseFormat
import Affjax (get)
import Affjax.Error (printError)
import Data.Either (Either)
import Effect.Aff (Aff)

getUrl :: String -> Aff String
getUrl url = do
  result <- get ResponseFormat.string url
  pure case result of
    Left err -> "Error: " <> printError err
    Right response -> response.body
This approach ensures type-safe handling of potential network errors without blocking the main thread. Record polymorphism enables functions to operate on dynamic records using row types, allowing extra fields without explicit enumeration. A function like showPerson can process records with required first and last fields, polymorphic over additional row r:
purescript
showPerson :: forall r. { first :: String, last :: String | r } -> String
showPerson rec = rec.last <> ", " <> rec.first
This works for minimal records { first: "John", last: "Doe" } or extended ones like { first: "John", last: "Doe", age: 30 }, providing flexibility for varying data shapes while enforcing core fields. Monad transformers allow stacking effects, such as combining StateT for mutable state with Effect for side effects in a simple state machine. Using the purescript-transformers library, a counter example demonstrates incrementing state within an effectful context:
purescript
import Control.Monad.State.Trans (StateT(..), runStateT, get, put)
import Effect ([Effect](/page/Effect))
import Effect.Console ([log](/page/Log))
import Data.Tuple ([Tuple](/page/Tuple))

counter :: StateT Int [Effect](/page/Effect) Unit
counter = do
  n <- get
  put (n + 1)
  liftEffect $ [log](/page/Log) $ "Count: " <> show (n + 1)

main :: [Effect](/page/Effect) (Tuple Int Unit)
main = runStateT counter 0
Here, liftEffect integrates [Effect](/page/Effect) actions like logging into the StateT computation, enabling a state machine that tracks and outputs increments safely. Foreign function interface (FFI) provides type-safe access to DOM APIs, bridging PureScript's purity with browser interactions. For example, to retrieve a DOM element by ID, declare an import and corresponding JavaScript export: PureScript:
purescript
import Effect (Effect)
foreign import getElementById :: [String](/page/String) -> Effect [Element](/page/Element)
JavaScript (FFI file):
javascript
export function getElementById(id) {
  return function() {
    return document.getElementById(id);
  };
}
This ensures compile-time checks on the element retrieval, preventing runtime type errors in web applications. Similar patterns apply to other DOM methods, maintaining PureScript's type guarantees. To optimize recursive list processing and prevent stack overflows, PureScript employs tail-recursive functions with accumulators. A tail-recursive length computation avoids deep by accumulating the count:
purescript
import Data.Array (tail)
import Data.Maybe (fromMaybe)

lengthTailRec :: forall a. Array a -> Int
lengthTailRec arr = go arr 0
  where
  go :: Array a -> Int -> Int
  go xs acc =
    case tail xs of
      Nothing -> acc
      Just xs' -> go xs' (acc + 1)
This pattern extends to folds and maps on large lists, ensuring constant stack usage regardless of input size.

Community and Ecosystem

Adoption and Use Cases

PureScript is primarily adopted for developing web frontends, leveraging its interoperability with JavaScript frameworks like through libraries such as purescript-react, which enables seamless integration into existing ecosystems. It also supports server-side applications via compilation or the Purerl backend for Erlang virtual machines, facilitating full-stack development with shared code between client and server. These capabilities make it suitable for building reliable, type-safe applications in domains requiring complex logic, such as user interfaces and backend services. Notable projects demonstrate its production viability across industries. Pursuit, the official package documentation search engine for PureScript, is itself implemented in PureScript, showcasing its use for ecosystem tooling. In finance, Juspay, a payments technology company, employs PureScript extensively for mobile app development and UI frameworks like Presto, reporting up to 10x faster development cycles for transactional applications due to its equation-like expressiveness and type safety. Arista Networks, a networking firm, maintains over 100,000 lines of PureScript in production for frontend applications, contributing tools like the purescript-backend-optimizer to enhance JavaScript output performance. Other examples include Lumi's use of PureScript with purescript-react-basic for frontend and a small amount of backend code in packaging software, and id3as's deployment of approximately 100,000 lines via Purerl for Erlang-based production systems over four years. PureScript's adoption remains niche within communities, though it trails mainstream languages in broader industry uptake. The 2023 community survey indicated that while 56% of former users cited large-scale corporate adoption as a deterrent, active production use persists among specialized teams, bolstered by ongoing contributions like backend optimizations. Key benefits include reduced runtime errors through strong static typing and pure functions, leading to more maintainable codebases, as evidenced by Juspay's for complex payment flows. However, drawbacks encompass a steep for developers unfamiliar with functional paradigms and a comparatively smaller ecosystem than or alternatives. Case studies highlight incremental adoption strategies, such as integrating PureScript modules into existing projects module-by-module to leverage without full rewrites, as practiced by teams at CollegeVine for components spanning 127,000 lines. Panoramic Software has similarly transitioned all new projects to PureScript, training developers to handle while phasing out backends.

Resources and Contributions

The official documentation for PureScript includes the "PureScript by Example" interactive book, which provides a structured introduction to the language through practical examples and exercises. The PureScript website offers tutorials covering core concepts, such as modules, types, and effects, aimed at beginners transitioning from other functional languages. Additionally, the Pursuit search engine serves as the primary API reference, indexing packages and documentation from the ecosystem to facilitate code discovery and type checking. Community discussions occur on the official forum, where users share questions, announcements, and feedback on language features. The PureScript server provides real-time chat for collaborative problem-solving and quick advice. The subreddit r/purescript hosts informal threads on best practices, library recommendations, and project showcases. Contributions to PureScript are coordinated through its repository, which includes detailed guidelines for submitting pull requests, reporting issues via the tracker, and adhering to the . Key focus areas encompass compiler optimizations, new FFI bindings for libraries, and enhancements to the standard library, with contributors encouraged to tackle labeled issues for newcomers. Learning paths begin with the online REPL at Try PureScript, allowing immediate experimentation without local setup. For development environments, guides detail integrating PureScript with VS Code using extensions for , type checking, and support. In 2025, the PureScript Analyzer reached public alpha in September, introducing features such as auto-completion, hover information, jump to definition, and find all references for enhanced integration. Additionally, June 2025 updates introduced statically-linked binary releases to simplify installation across platforms. PureScript lacks dedicated annual conferences, but its developers and users participate in meetups and talks at events like LambdaConf and local Haskell/Purescript groups.

References

  1. [1]
    PureScript
    PureScript. A strongly-typed functional programming language that compiles to JavaScript. Benefits. Compile to readable JavaScript and reuse existing ...PureScript by Example · Try PureScript! · Pursuit · Discourse
  2. [2]
    PureScript by Example: Foreword
    This book will show you how to get started with the PureScript programming language, from the basics (setting up a development environment) to the advanced.Introduction · Getting Started · Type Classes · Functions and Records
  3. [3]
    Functional Geekery Episode 26 – Phil Freeman
    Sep 15, 2015 · First version released around end of 2013 SlamData. Sum Types vs Product Types PureScript compilation going directly to JavaScript JavaScript ...
  4. [4]
    Introduction - PureScript by Example
    PureScript provides the ability to import existing JavaScript code, by providing types for its values and functions, and then to use those functions in regular ...Polyglot Web Programming · How To Read This Book · Getting Help
  5. [5]
    Purescript-native can now target Golang
    purescript-native now supports both Go and C++11 as intermediate languages. The purescript-native “suite” comprises two utilites: pscpp and psgo.
  6. [6]
    purescript/purescript: A strongly-typed language that ... - GitHub
    A small strongly typed programming language with expressive types that compiles to JavaScript, written in and inspired by Haskell.
  7. [7]
    Releases · purescript/purescript - GitHub
    Sep 28, 2024 · Releases: purescript/purescript ; v0.15.16-7. Jun 26 · v0.15.16-7 · 9dd761a · on Jun 26, 2025, 10:21 PM ; v0.15.16-6. Jun 22 · v0.15.16-6 · 2b7164f.
  8. [8]
    Read PureScript by Example | Leanpub
    I am the original developer of the PureScript compiler. I'm based in Los Angeles, California, and started programming at an early age in BASIC on an 8-bit ...Missing: history creator
  9. [9]
    purescript-language-purescript - Pursuit
    Sep 9, 2023 · This repo ports part of the code from PureScript, which is licensed under BSD-3-Clause and made possible by its amazing contributors.
  10. [10]
  11. [11]
    PureScript Language Forum
    A place for PureScript users to ask questions and socialize.Announcements · Purescript-analyzer · Purescript-native can now...
  12. [12]
    [PDF] PURESCRIPT IN WEB DEVELOPMENT - Trepo
    Apr 24, 2024 · This design philosophy allows PureScript to add qualities to JavaScript that it would otherwise lack. For example, due to JavaScript's weak ...
  13. [13]
    PureScript: A Haskell-like Language that Compiles to JavaScript
    Sep 22, 2014 · Furthermore, says PureScript creator Phil Freeman, PureScript provides interoperability with other languages which target JavaScript.
  14. [14]
    PureScript by Example by Phil Freeman [Leanpub PDF/iPad/Kindle]
    Sep 24, 2017 · PureScript is a small strongly, statically typed programming language with expressive types, written in and inspired by Haskell, and compiling ...Missing: 2013 | Show results with:2013
  15. [15]
    purescript-contrib/pulp: A build tool for PureScript projects - GitHub
    pulp is not a package manager, only a build tool. The PureScript community has standardised on Bower as the default package manager, but there are alternatives ...Missing: introduction | Show results with:introduction
  16. [16]
    TypeScript vs. PureScript - LogRocket Blog
    Mar 7, 2019 · PureScript is a language created by Phil Freeman in 2013, and it's maintained by the community. It is a strict, purely functional language ...
  17. [17]
    Better know a language: PureScript - taylor.fausak.me
    This is also known as row polymorphism, structural typing, or static duck typing. And it's the coolest thing about PureScript. fullName ...
  18. [18]
    PureScript 0.13 Release - Announcements - PureScript Discourse
    There is a new compiler release! I'm very excited about this release as it includes a lot of internal changes for the grammar and parser, ...Missing: FFI | Show results with:FFI
  19. [19]
    PureScript v0.15.0 Released - Announcements
    Apr 29, 2022 · I'm excited and proud to announce the release of PureScript 0.15! npm i -g purescript. If you are new to PureScript, you can follow the ...
  20. [20]
    PureScript v0.13.8 released! - Announcements
    May 23, 2020 · PureScript v0.13.8 is out! Highlights include some really nice performance improvements and the new purs graph command, amongst others.
  21. [21]
    PureScript v0.15.14 released - Announcements
    Jan 3, 2024 · PureScript v0.15.14 has been released! This release addresses some high-memory-usage issues in the compiler Thank you all who contributed!
  22. [22]
    Future PureScript releases are now statically-linked
    Future PureScript releases are now statically-linked · Announcements · compiler · PureFunctor June 27, 2025, 5:43am #1 ... Releases tab of the ...Missing: 0.15.16 | Show results with:0.15.16<|separator|>
  23. [23]
    purescript/pursuit: Website for hosting and searching ... - GitHub
    Pursuit hosts API documentation for PureScript packages. It lets you search by package, module, and function names, as well as approximate type signatures.Missing: 2014 | Show results with:2014
  24. [24]
    npm:purescript-spago - Skypack
    Dec 16, 2018 · PureScript package manager and build tool powered by Dhall and Spacchetti package-sets.
  25. [25]
    What exactly is considered a breaking change to a PureScript library?
    Mar 6, 2017 · The Rust community has a fairly detailed description of their interpretation of Semantic Versioning. The PureScript community has this, which ...Missing: adoption | Show results with:adoption
  26. [26]
    documentation/language/Syntax.md at master · purescript/documentation
    Insufficient relevant content. The provided URL content is a GitHub page with navigation, feedback, and footer information but does not contain the PureScript syntax documentation from `Syntax.md`. No details on modules, imports/exports, data declarations, function definitions, indentation, pattern matching, guards, do-notation, or semantics like evaluation strategy or immutability are available.
  27. [27]
    The Effect Monad - PureScript by Example
    The Effect monad is a fundamental tool in real-world PureScript code. It will be used in the rest of the book to handle side-effects in a number of other use- ...
  28. [28]
    Lazy vs Strict - PureScript: Jordan's Reference
    Lazy vs Strict. A computation can either be lazy or strict. Before giving the below table, let's give a real-life example. This is "Strict evaluation.Missing: strategy | Show results with:strategy
  29. [29]
    Functions and Records - PureScript by Example
    This chapter will introduce two building blocks of PureScript programs: functions and records. In addition, we'll see how to structure PureScript programs.
  30. [30]
    Fear, trust and PureScript: Building on trust with types and functional ...
    Mar 7, 2018 · By default in PureScript all data is immutable, so instead of writing code like the mutable example from JavaScript, you might write this:.
  31. [31]
    Academic / theoretical basis of the PureScript type system - language
    Apr 29, 2019 · This post aggregates papers which are useful for understanding the PureScript type system. Note: while these papers inspired or relate to PureScript type ...
  32. [32]
    Pattern Matching - PureScript by Example
    This function is polymorphic in the row r of record fields, hence the name row polymorphism. Note that this behavior is different than that of the original ...Missing: 0.7 2015<|separator|>
  33. [33]
    Type Classes - PureScript by Example
    In this chapter, we've been introduced to type classes, a type-oriented form of abstraction that enables powerful forms of code reuse.Missing: qualified | Show results with:qualified
  34. [34]
    None
    ### Summary of Type Classes, Higher-Kinded Types, and Kinds in PureScript
  35. [35]
    None
    ### Summary of Typed Holes
  36. [36]
    Recursion, Maps And Folds - PureScript by Example
    In this chapter, we will look at how recursive functions can be used to structure algorithms. Recursion is a basic technique used in functional programming.Infix Operators · Array Comprehensions · Do Notation<|control11|><|separator|>
  37. [37]
    Data.Array - Pursuit - PureScript
    This module is useful when integrating with JavaScript libraries which use arrays, but immutable arrays are not a practical data structure for many use cases.
  38. [38]
    purescript-effect - GitHub
    This package provides the standard type PureScript uses to handle native effects, ie effects which are provided by the runtime system, and which cannot be ...
  39. [39]
    purescript-eff - Pursuit
    Oct 3, 2020 · As of PureScript 0.12 the default type for handling effects is Effect from purescript-effect . This differs from Eff by removing the row of ...Missing: based | Show results with:based
  40. [40]
    Why did PureScript go from Eff to Effect?
    Eff rows were not sufficiently useful in terms of actually guaranteeing what effects were run in a given Eff row , and the attempts to track possible exceptions ...
  41. [41]
    Asynchronous Effects - PureScript by Example
    This chapter focuses on the Aff monad, which is similar to the Effect monad, but represents asynchronous side-effects.
  42. [42]
    Monadic Adventures - PureScript by Example
    The goal of this chapter will be to learn about monad transformers, which provide a way to combine side-effects provided by different monads.
  43. [43]
    purescript/purescript-st: The ST effect, for safe local mutation - GitHub
    The ST effect, for safe local mutation. Contribute to purescript/purescript-st development by creating an account on GitHub.Missing: guide | Show results with:guide
  44. [44]
    PureScript Programming Language Compiler - Hackage
    Feb 7, 2024 · A small strongly, statically typed programming language with expressive types, inspired by Haskell and compiling to JavaScript.
  45. [45]
    How does Purescript Compiler work? - Help
    Jun 10, 2020 · #2. I would describe the compiler pipeline as follows: Tokenize -> Parse CST -> Convert to AST -> Desugar -> Type check -> Convert to CoreFn ...Missing: architecture stages
  46. [46]
    Dead code elimination should happen before codegen #2970 - GitHub
    Jul 6, 2017 · We can do dead code elimination in new backends · The graph traversal code can be more reliable since it works on the more principled functional ...Missing: beta | Show results with:beta
  47. [47]
    Can pure expression be reduced/inlined and optimized at compile ...
    Sep 8, 2020 · PureScript can theoretically implement that (and might do so at some point). But almost every JavaScript minifier will do that for you. And if ...Missing: improvements | Show results with:improvements
  48. [48]
    Incremental builds are slow - PureScript Discourse
    Mar 18, 2019 · As far as incremental speed for full builds, purescript-cst will offer about a 6-7x improvement in parsing speed, which is a pretty decent speed ...
  49. [49]
    purescript-language-server - NPM
    Jun 11, 2025 · The purescript-language-server comes with support for formatting PureScript code via several external tools, exposed as a standard LSP ...<|control11|><|separator|>
  50. [50]
    The Foreign Function Interface - PureScript by Example
    This chapter will introduce PureScript's foreign function interface (or FFI), which enables communication from PureScript code to JavaScript code and vice versa ...
  51. [51]
    purescript/spago: PureScript package manager and build tool - GitHub
    This documentation concerns the new PureScript rewrite of Spago. If you are looking for the Haskell codebase (versions up to 0.21.x), please head over to the ...Missing: 2018 | Show results with:2018
  52. [52]
    purerl/purerl: Erlang backend for the PureScript compiler - GitHub
    The purerl backend requires the mainline PureScript compiler ( purs ) to operate. It generates .erl source files from the CoreFn purs compiler output (and ...
  53. [53]
    documentation/guides/FFI.md at master · purescript/documentation
    Insufficient relevant content. The provided text is a GitHub page header and navigation menu, with no substantive information about the Foreign Function Interface (FFI) or its details in PureScript. No examples, type-safe aspects, or JavaScript-specific content are present.
  54. [54]
    React Bindings for PureScript - GitHub
    Low-level React bindings for PureScript. For a higher-level library for working with React, please see react-basic repository.Missing: interoperability | Show results with:interoperability
  55. [55]
    PureScript
    ### Summary of Core PureScript Libraries
  56. [56]
    Prelude - Pursuit - PureScript
    Prelude is a module that re-exports many other foundational modules from the purescript-prelude library (e.g. the Monad type class hierarchy, the Monoid type ...Missing: core | Show results with:core
  57. [57]
    purescript-spec/purescript-spec: Testing framework for Purescript
    PureScript Spec is a testing framework for Purescript, inspired by hspec. ... Integration tests #138; Better, more honest support for failFast #140. 7.4.1.Missing: Jest interop
  58. [58]
    Pursuit - PureScript
    Pursuit hosts API documentation for PureScript packages. It lets you search by package, module, and function names, as well as approximate type signatures.Purescript-aff · Purescript-prelude · Purescript-datetime · Purescript-fetchMissing: 2014 | Show results with:2014
  59. [59]
    purefunctor/purescript-analyzer: Compiler frontend for ... - GitHub
    purescript-analyzer is a compiler frontend for the PureScript programming language. Contributing. The project uses the following development dependencies, ...Missing: alpha September
  60. [60]
  61. [61]
    purescript-dom-simple - Pursuit
    Apr 15, 2024 · createElement or document.getElementById or such) quite verbose. This library attempts to do a lot less in a more practical fashion. Usage.
  62. [62]
    PureScript in Industry - Help
    Mar 8, 2023 · Our backend is Rails (I know, don't ask), and the rough policy is to start with Rails/HAML for simple/dumb UIs, but as soon as it needs to be ...
  63. [63]
    10x Faster Mobile App Dev with PureScript - Vimal Kumar, Juspay
    Sep 18, 2017 · At Juspay, after spending a few years writing various payments applications, we found ourselves looking for a better way.
  64. [64]
    Do you have a PureScript app in production?
    We're using PureScript in production at Lumi for both front-end and a small amount of back-end code. We use our purescript-react-basic library on the ...
  65. [65]
    The State of PureScript Survey 2023 - The Results Are In!
    May 24, 2023 · PureScript's second annual survey revealed some key trends about the language and the broader context of functional programming.The Purescript 2023 Survey · Tooling · Documentation
  66. [66]
    aristanetworks/purescript-backend-optimizer - GitHub
    PureScript's built-in optimizer leaves a lot on the table by only performing naive syntactic rewrites in the JavaScript specific backend.