Recent from talks
Nothing was collected or created yet.
Joy (programming language)
View on WikipediaThis article possibly contains original research. (May 2009) |
| Joy | |
|---|---|
| Paradigm | multi-paradigm: functional, concatenative, stack-oriented |
| Designed by | Manfred von Thun |
| Developer | Manfred von Thun John Cowan |
| First appeared | 2001 |
| Stable release | March 17, 2003
/ March 17, 2003 |
| Typing discipline | strong, dynamic |
| Major implementations | |
| Joy0, Joy1, "Current Joy", "John Cowan's Joy", "JoyJ (Joy in jvmm)" | |
| Influenced by | |
| Scheme, FP, Forth | |
| Influenced | |
| Factor, Cat, V, Trith | |
The Joy programming language in computer science is a purely functional programming language that was produced by Manfred von Thun of La Trobe University in Melbourne, Australia. Joy is based on composition of functions rather than lambda calculus. It was inspired by the function-level programming style of John Backus's FP.[1] It has turned out to have many similarities to Forth, due not to design but to an independent evolution and convergence.[citation needed]
Overview
[edit]Functions in Joy lack formal parameters. For example, a function that squares a numeric input can be expressed as follows:[2]
DEFINE square == dup * .
In Joy, everything is a function that takes a stack as an argument and returns a stack as a result. For instance, the numeral '5' does not represent an integer constant, but instead a short program that pushes the number 5 onto the stack.
- The dup operator simply duplicates the top element of the stack by pushing a copy of it.
- The * operator pops two numbers off the stack and pushes their product.
So the square function makes a copy of the top element, and then multiplies the two top elements of the stack, leaving the square of the original top element at the top of the stack, with no need for a formal parameter. This makes Joy concise, as illustrated by this definition of quicksort:[3]
DEFINE qsort == [small] [] [uncons [>] split] [swapd cons concat] binrec.
Mathematical purity
[edit]Joy is a concatenative programming language: "The concatenation of two programs denotes the composition of the functions denoted by the two programs".[4]
See also
[edit]References
[edit]- ^ Manfred von Thun (December 12, 2003). "A Conversation with Manfred von Thun". Retrieved May 31, 2013.
In the early 1980s I came across the famous Backus paper "Can programming be liberated from the von Neumann style," and I was immediately intrigued by the higher level of programming in his FP.
- ^ "An informal tutorial on Joy". Archived from the original on October 7, 2011.
- ^ "Sequence Library". Archived from the original on October 7, 2011.
- ^ "Mathematical Foundations of Joy". Archived from the original on October 7, 2011.
External links
[edit]- Official Joy Programming Language Website (La Trobe University) at the Wayback Machine (archived 2012-09-07)
- Joy homepage mirror
- Joy source code (GitHub-Archive)
- Freneger, Paul (August 2003). "The JOY of forth". ACM SIGPLAN Notices. 38 (8): 15–17. doi:10.1145/944579.944583.
- von Thun, Manfred; Thomas, Reuben (October 9, 2001). "Joy: Forth's Functional Cousin" (PDF). Proceedings of the 17th EuroForth Conference.
- Christopher Diggins (December 31, 2008). "What is a Concatenative Language". Dr. Dobbs.
- Apter, Stevan. "Functional Programming in Joy and K". Vector. Archived from the original on 2008-08-28. Retrieved 2011-02-28.
- mjoy, an interpreter in Lazarus for drawings with turtle graphics (Subset of Joy)
- Joy of Postfix Calculator App (Subset of Joy)
Joy (programming language)
View on Grokipedia2 3 + computes 5 on the stack), and the use of quotation (enclosed in [ ]) to treat programs as first-class data that can be manipulated by combinators like i (identity), dip (temporary stack manipulation), and map (application to list elements).[1] Recursion is handled through dedicated combinators such as linrec for linear cases and binrec for binary ones, avoiding explicit recursive definitions.[4] Unlike stack-oriented languages like Forth, Joy is purely functional and treats lists and programs uniformly as stackable entities, enabling powerful higher-order programming without environments or assignments.[4]
Joy's influence extends to modern concatenative languages and serves as a minimalistic tool for studying functional programming principles, with open-source implementations available in languages like C, Python, and Java.[3] Its design underscores the expressive power of function composition, making complex algorithms concise while fostering an intuitive understanding of computation as state transformation.[1]
Introduction
Overview
Joy is a purely functional programming language developed by Manfred von Thun of La Trobe University, characterized by its concatenative and stack-oriented design.[4] Unlike conventional functional languages that apply functions to explicit arguments via lambda abstraction, Joy focuses on the direct composition of functions, treating programs as sequences that manipulate a data stack to produce results.[5] This approach enables highly concise expressions of computations, drawing on principles from combinatory logic to avoid variables and formal parameters altogether.[4] The language evolved from von Thun's earlier work on logic programming in the 1980s, with initial implementations emerging around 1995 and significant refinements continuing into the early 2000s.[4] A key tutorial on Joy was presented at the EuroForth conference in 2001, marking an important milestone in its documentation and dissemination.[5] Implementations include the original C-based interpreter, alongside versions in ML and Scheme developed by other contributors; modern open-source implementations are available in languages including Python, Java, and C++.[4][6][7] Joy's stack-based execution model supports point-free programming, where functions transform the stack without referencing named variables, facilitating mathematical clarity and ease of program manipulation.[5] Primarily employed in academic research and experimental programming, Joy has influenced discussions on concatenative paradigms but remains niche, without broad commercial adoption.[4]Key characteristics
Joy is distinguished by its complete absence of variables, assignments, and formal parameters, with all data manipulation performed exclusively through an implicit stack that serves as the primary mechanism for passing arguments and returning results. This design eliminates the need for explicit bindings or substitutions, allowing functions to operate directly on stack elements without reference to named entities, thereby simplifying the language's semantics and avoiding issues associated with variable scoping and mutation.[5][8][4] The language is dynamically typed, with type checking occurring automatically at runtime through type-specific operators for data such as integers, characters, truth values, and aggregates like sets, strings, and lists. This approach provides runtime type verification without requiring compile-time declarations, supporting polymorphic operations that adapt to the stack's contents.[5][8] Joy's execution model is referentially transparent, with pure functions that produce no side effects, guaranteeing that the output depends solely on the input stack state and enabling predictable composition of program fragments. This purity stems from the language's functional foundations, where every program denotes a transformation of the stack without altering external state.[5][4][8] Programs in Joy consist of sequences of words—literals, defined functions, or built-in operations—that compose functions directly through concatenation, leveraging the concatenative paradigm to build complex behaviors from simple juxtapositions without explicit application syntax. This compositional style facilitates modular and readable code, as appending words inherently chains their effects on the stack.[5][8] Higher-order functions are supported through quotations, which encapsulate program fragments as first-class data types on the stack, allowing combinators to execute, map, or otherwise manipulate these quoted programs to generate and transform other programs dynamically. Quotations thus enable metaprogramming capabilities, such as defining functions that operate on code structures, enhancing Joy's expressiveness in handling abstractions.[5][4][8]History
Development
Joy was developed by Manfred von Thun, a philosopher and computer scientist at La Trobe University in Melbourne, Australia.[4] His work on the language began in the early 1980s as an exploration of function composition alternatives to lambda calculus, drawing inspiration from John Backus's 1977 Turing Award lecture on functional programming and von Thun's experiences teaching deductive logic, philosophy of science, and introductory programming courses.[4] Initially prototyped in Prolog and later rewritten in C around 1995, Joy evolved from von Thun's interests in logic programming and stack-based computation, aiming to simplify expression through quotations and combinators without variables or explicit recursion.[4] The language first appeared publicly in 2001, coinciding with von Thun's presentation of an informal tutorial at the EuroForth conference, which highlighted its concatenative and functional design.[5] That year, collaboration with programmer John Cowan significantly advanced the project; Cowan contributed features such as file I/O, floating-point support, access to C library functions, and integration with the Boehm-Demers-Weiser garbage collector, resulting in a more robust implementation.[4] This partnership led to the stable release on March 17, 2003, marking a key milestone in Joy's maturation.[3] Joy's evolution included several versions: early prototypes like Joy0 and Joy1, followed by "Current Joy" as the primary interpreter, and "John Cowan's Joy" as an extended variant incorporating his enhancements.[3] Additional contributions, such as those from Nick Forde, refined the system in the early 2000s, but documentation on maintenance remains sparse. Manfred von Thun died on 23 October 2011.[2] No significant updates have occurred since 2003, and as of 2025, the language's development appears dormant, with activity limited to archival mirrors and community discussions.[3]Influences
Joy's design draws primarily from John Backus's FP system, developed in the late 1970s and early 1980s, which emphasized function composition as a means to achieve higher-level programming without reliance on lambda calculus or variable binding.[9] Backus's work, outlined in his 1977 Turing Award lecture, inspired Joy's creator, Manfred von Thun, to pursue a purely functional approach that liberates programming from imperative styles and von Neumann architecture constraints.[4] This influence is evident in Joy's avoidance of formal parameters and its focus on composing functions to build programs, extending FP's algebraic structure into a stack-based paradigm.[5] Forth provided the operational foundation for Joy through its stack-based execution and postfix notation, which enable concise expression of computations without parentheses or explicit variables.[10] Originating in the 1970s, Forth's model of pushing data onto a stack and applying operators sequentially directly shaped Joy's syntax, such as expressions like2 3 + to compute sums.[10] However, Joy extends this style functionally by eliminating Forth's imperative elements, like mutable state and dictionary lookups, in favor of immutable quotations and combinators for program manipulation.[4]
Scheme contributed ideas around higher-order functions and the treatment of code as data through quotations, which Joy adapts to its stack semantics for defining and executing programs dynamically.[4] In Scheme, procedures can be passed as arguments or returned as values, a concept Joy mirrors by allowing quoted programs to be stacked, mapped over data, or composed without lambda abstractions.[5] This adaptation enables Joy to handle recursion and iteration via combinators like map and i, providing a variable-free alternative to Scheme's parameter-based functions.[11]
Combinatory logic, pioneered by Moses Schönfinkel and Haskell Curry, serves as a conceptual foundation for Joy's elimination of variables and parameters, allowing programs to be expressed solely through function composition and basic combinators.[4] Similarly, category theory informs Joy's emphasis on morphisms as compositions, aligning it with categorical languages that prioritize relational structures over internal object details.[11] Though not direct implementations, these mathematical ideas underpin Joy's algebra, enabling simple, manipulable programs without environmental dependencies.[5]
Overall, Joy integrates these influences by combining Forth's operational simplicity and stack orientation with FP's functional purity and the higher-order capabilities inspired by Scheme, while grounding the result in combinatory and categorical principles to avoid imperative pitfalls.[4] This synthesis yields a language where programs are quotations executed via composition, offering a concise yet expressive alternative to traditional functional paradigms.[10]
Design and philosophy
Concatenative paradigm
Joy is a concatenative programming language, a paradigm in which the concatenation of program texts directly corresponds to the composition of the functions they denote. In this approach, juxtaposing two programs P and Q, written as PQ, signifies applying the function represented by P followed by the function represented by Q, leveraging the associativity of composition for modular program construction.[5][1] This paradigm offers several advantages, particularly in enabling point-free or tacit programming styles that eliminate the need for explicit variable names and formal parameters. By avoiding variable bindings, Joy programs become more concise and reusable, as components can be composed without concern for naming conflicts or substitution rules typical in other functional languages.[12][13] Additionally, the compositional nature promotes algebraic reasoning, where equivalent subprograms can be substituted seamlessly to refactor or optimize code.[1] Central to Joy's concatenative model is its reliance on a data stack, where programs act as transformations that consume elements from the stack and produce new ones, preserving overall stack effects through composition. For instance, if P transforms the stack in a certain way and Q does so subsequently, the combined PQ maintains predictable stack behavior, facilitating modular design without explicit type annotations for arguments. This stack-oriented composition ensures that all functions in Joy are inherently of the type Stack → Stack, treating the entire stack as the implicit input and output medium.[5][1] In contrast to applicative languages, which rely on explicit function application and lambda abstractions (as in Lisp or Haskell), Joy eschews such mechanisms in favor of direct composition and quotations for higher-order functions. This avoids the complexities of environments and beta-reduction, instead drawing from combinatory logic to achieve functionality through stack manipulations alone, resulting in a purer form of function-level programming.[5][13]Stack-based execution
Joy's execution model relies on a single data stack that serves as the primary structure for holding and manipulating all data during program execution. Literals, such as numbers or lists, push values onto the top of this stack, while operators consume one or more elements from the top, perform computations, and push results back onto the stack. This postfix evaluation ensures that programs are processed sequentially without explicit variable assignments, as all data passing occurs implicitly through stack manipulations.[14][15] To describe the behavior of stack operations, Joy employs a notation that indicates the stack state before and after execution, typically written as "before -- after," where elements are represented symbolically (e.g.,dup: a -- a a duplicates the top element a, leaving two copies on the stack). This convention highlights how each operation transforms the stack without side effects, maintaining the language's functional purity. For instance, arithmetic operators like addition follow + : a b -- a+b, popping two numbers and pushing their sum. Such notation facilitates understanding the unary nature of Joy functions, each taking the entire stack as input and producing a new stack as output.[16][15]
Control structures in Joy are implemented through combinators that operate on quoted programs stored on the stack, rather than traditional primitives like loops or conditionals. For example, the binrec combinator enables binary recursion by consuming four quotations representing base cases and recursive steps, applying them to stack elements to build complex computations such as sorting algorithms. Similarly, i executes a quotation directly, while ifte selects between two quotations based on a boolean flag on the stack. These mechanisms compose control flow from basic stack operations, avoiding explicit recursion or iteration constructs.[16][15]
Accessing elements deeper in the stack is managed via specialized operations that rearrange or copy items without relying on named variables. The rolldown operation rotates the top three elements (e.g., a b c -- b c a), roll moves the third to the top (a b c -- c a b), and pick copies the nth element to the top (e.g., with n=2 on a stack [... x2 x1 x0 2] -- [... x2 x1 x0 2 x1]). These tools allow programmers to manipulate buried data efficiently, supporting the deep stacks that arise in complex expressions.[16][15]
Error handling in Joy occurs dynamically at runtime, with stack underflow—attempting to pop from an empty or insufficient stack—triggering an exception or halt, as seen in implementations reporting "Data stack underflow." Type mismatches, such as applying a numeric operator to non-numbers, are similarly detected and raise errors based on the underlying interpreter's checks, ensuring operations fail explicitly rather than producing undefined behavior. Combinators like ifinteger provide typed conditional execution to mitigate such issues proactively.[17][15]
Syntax and semantics
Basic syntax
Joy's basic syntax is minimalist and postfix-oriented, relying on an implicit data stack for computation without variables, assignments, or explicit control flow beyond quotations. Programs consist of sequences of lexical elements executed strictly left-to-right, where each element either pushes data onto the stack, manipulates the stack, or consumes stack items to produce results. The language lacks traditional statements, blocks, or semicolons; instead, execution proceeds linearly until the end of input or an explicit terminator like the dot (.) in interactive or batch modes, which often triggers stack inspection or output.[5][15]
Lexical elements in Joy include words, literals, and grouping symbols. Words are alphanumeric identifiers or operator symbols representing functions, such as predefined stack manipulation primitives like dup (which duplicates the top stack item) or arithmetic operators like + (which adds the top two stack items); these can be user-defined as well. Literals provide immediate values and include integers (e.g., 42 or -10), floats (e.g., 3.14 or 1.5e2), booleans (true and false), strings delimited by double quotes (e.g., "hello" with support for escapes like \n), characters in single quotes (e.g., 'a'), and aggregate types like lists enclosed in square brackets (e.g., [1 2 3], functioning as arrays or vectors). Symbols such as square brackets [ and ] denote quotations, which capture unevaluated sequences for later execution or as data.[5][15][14]
Definitions allow users to extend the language by naming compound expressions, using the syntax name == body, where name is a word identifier and body is any valid sequence of lexical elements ending implicitly at the next definition or input end; in the interactive interpreter, this is often prefixed with DEFINE and terminated by . to register the word in the dictionary. For example, a definition for squaring a number might be square == dup *, which duplicates the top stack item and multiplies it by itself. Recursive or higher-order definitions are supported without special syntax, relying on quotations for parameterization.[5][15][14]
Joy recognizes a small set of primary data types, all uniformly treated as stack-manipulating functions in its functional paradigm: integers and floats for numeric computation, booleans for logical operations, strings and characters for textual data, lists (serving as arrays or sequences) for collections, sets for unordered unique elements (e.g., {1 2 3}), and quotations as first-class reified programs. Type checking is dynamic and implicit, with most operations polymorphic where possible but raising errors on mismatches, such as applying arithmetic to non-numerics. Stack manipulation words like dup and swap operate uniformly across types to enable composition.[5][15]
Quotations and combinators
In Joy, quotations are enclosed in square brackets[ ] and serve as first-class values on the stack, encapsulating sequences of values, literals, or programs that can be manipulated and executed dynamically.[15] These structures enable higher-order programming by treating code fragments as data, allowing them to be composed, stored in aggregates, or passed to combinators without the need for explicit function definitions or variables.[5] For instance, a quotation like [dup *] represents a program snippet that duplicates the top stack element and multiplies it by itself, which remains inert until activated.[15]
The combinator i executes a quotation directly, consuming it from the stack and applying its contents sequentially.[15] Its stack effect is [P] → ..., where [P] denotes the quotation and ... indicates the result of executing P. For example, with a number 5 on the stack, [dup *] i computes 25 by duplicating 5 and multiplying.[5] Other essential combinators include dip, which temporarily saves the top stack item X, executes the quotation [P], and then restores X, with effect X [P] → ... X; this facilitates operations under the top element, such as [swap] dip to exchange items while preserving context.[15] The map combinator applies a quotation to each element of an aggregate like a list, producing a new aggregate; its effect is A [P] → B, as in [1 2 3] [dup *] map yielding [1 4 9].[5] For binary recursion, binrec uses four quotations—test quotation [B], terminal case [T], and recursive steps [R1] and [R2]—to process data conditionally, enabling algorithms like quicksort without explicit loops.[15]
Logical operations in Joy are constructed using combinators that operate on boolean quotations. The eq combinator checks equality between two stack items X and Y, pushing a boolean result with effect X Y → B.[15] This integrates with ifte, a conditional combinator taking three quotations—condition [B], then-branch [T], and else-branch [F]—executing [T] if [B] yields true, otherwise [F], with effect [B] [T] [F] → ....[5] For example, [1000 >] [2 /] [3 *] ifte halves the top number if it exceeds 1000, else triples it, demonstrating how quotations enable branching without traditional control structures.[15]
Recursion in Joy avoids explicit fixpoints by leveraging combinators that unfold quotations iteratively. The primrec combinator performs primitive recursion on a number X or aggregate, using an initial quotation [I] and a combining step [C], with effect X [I] [C] → R; for instance, 5 [1] [*] primrec computes the factorial 120 by successive multiplication.[5] Similarly, genrec supports general recursion with quotations for test quotation [B], terminal case [T], and recursive bodies [R1] and [R2], allowing flexible definitions like tree traversals.[15] An illustrative effect is dup, which duplicates a quotation [a] on the stack, resulting in [a] [a], highlighting their role in duplicating and composing program fragments.[15]
Mathematical foundations
Functional purity
Joy's core design adheres strictly to functional purity, where all functions are pure, meaning their outputs depend solely on their inputs without any side effects, mutable state, or interaction with external environments.[5] In this paradigm, programs are constructed as compositions of functions that operate on an implicit stack, ensuring that each operation transforms the stack immutably from one state to another.[18] For instance, a simple function likesquare defined as dup * duplicates the top stack element and multiplies it by itself, producing a result purely from the input without altering any global state.[5] This purity is foundational, as Joy eschews variables, assignments, and lambda abstractions, relying instead on a variable-free notation that eliminates the need for parameter passing or binding.[18]
Referential transparency is a direct consequence of this purity, allowing any expression to be substituted with an equivalent one without changing the program's meaning, which facilitates equational reasoning and optimizations.[18] In Joy, equivalences such as 2 * == dup + for doubling a number enable straightforward program rewriting, as there are no hidden dependencies on external state.[18] This transparency simplifies program verification and debugging, as developers can reason about code through algebraic manipulations rather than tracking side effects.[5]
The implications of functional purity extend to enhanced parallelism and compositional safety. Without mutable state or side effects, Joy programs can be executed concurrently, as operations like map or fold on aggregates can process elements independently using combinators such as constr12 for parallel computation of sums and sizes.[5] Purity ensures that function concatenation—Joy's primary mechanism for building programs—remains interference-free, allowing seamless integration of components without unintended interactions.[18] However, input/output operations introduce impurities through dedicated primitives like get for reading input and put for writing output, which are handled outside the core computational model to maintain the language's purity in its foundational semantics.[5] These extensions are typically used in libraries or at program boundaries, preserving the core's referential transparency during computation.[18]
Connections to category theory
Joy's connections to category theory arise primarily from its stack-based semantics and concatenative nature, which lend themselves to formal interpretations in categorical terms. In this framework, the category of stack states—where objects are configurations of the program stack (sequences of values)—has Joy programs as morphisms. These morphisms transform one stack state to another, with composition defined by program concatenation, which corresponds to the sequential application of functions on the stack. This structure forms a monoid under concatenation in the syntactic domain and under function composition in the semantic domain, with the identity program[] (the empty quotation) serving as the unit element. The meaning function mapping syntactic programs to semantic functions is a monoid homomorphism, preserving this structure.[1]
Quotations in Joy, which are programs treated as data on the stack, can be viewed as endofunctors on this stack category. For instance, the LIST quotation acts as a functor that maps stack elements to lists, enabling operations like mapping over collections while preserving the categorical structure. Natural transformations emerge in algebraic manipulations of Joy programs; the reverse combinator, for example, serves as a natural transformation between the list functor and its iterated application, satisfying properties such as [reverse] dip map == map reverse, where dip applies a quoted program around another. This allows Joy to express higher-order concepts without explicit variables, aligning with functorial semantics.[19]
The language's combinators, such as dup (duplicating the top stack element) and swap (exchanging the top two elements), endow the stack category with a Cartesian closed structure. This enables point-free modeling of lambda abstraction, where function spaces and products are represented implicitly through stack manipulations, mirroring the Cartesian closed categories that provide a categorical semantics for the lambda calculus. Joy's reliance on these combinators facilitates the elimination of recursion and substitution in a purely compositional manner, akin to denotational semantics in closed categories.[1]
Furthermore, Joy embodies principles of combinatory logic within its stack-based paradigm, implementing variants of the SKI combinators adapted to stack operations. The identity combinator i dequotes a quotation directly ([P] i == P), while branching combinators like b (for composition) and dip (for dipping under arguments) replicate the functionality of S and K combinators, but operate on stack transformations rather than term applications. This integration provides a concrete realization of combinatory logic's variable-free style, with the stack serving as the medium for beta-reduction equivalents through quotation and execution. These elements collectively position Joy as a practical embodiment of abstract categorical and combinatory structures.[1][19]
Manfred von Thun formalized these mathematical semantics in his foundational works, establishing Joy's theoretical underpinnings through monoidal and categorical lenses, which distinguish it from applicative functional languages.[1][19]
Implementations
Original implementation
The original implementation of the Joy programming language was developed by Manfred von Thun and written in unadorned C, beginning in 1995, with earlier prototypes implemented in Pascal, Prolog, and Lisp.[4][20] This C-based interpreter translates external ASCII-form programs into internal tree code for execution, incorporating garbage collection to manage quotations—quoted programs treated as data structures—with an initial custom collector later replaced by the Boehm-Demers-Weiser (BDW) collector in 2001.[4] It supports an interactive read-eval-print loop (REPL) for direct command input and experimentation, alongside batch processing capabilities.[20] Key versions include the early Joy1 interpreter and the more mature "Current Joy" released in March 2003, which incorporated significant extensions such as file I/O, floating-point arithmetic, and interfaces to C functions contributed by John Cowan in early 2001, as well as additional enhancements by Nick Forde.[4][20] The implementation provides built-in primitives for arithmetic operations (e.g., addition, multiplication), logical operations (e.g., equality, conjunction), and input/output handling, while allowing extensibility through user-defined combinators and libraries written in Joy itself, such as seqlib.joy for sequence manipulation on lists and strings.[3][20] Self-hosting elements are feasible, as demonstrated by metacircular interpreters implemented in Joy code.[3] Initially distributed via Manfred von Thun's page at La Trobe University, the source code and libraries were freely available for download, including user libraries like usrlib.joy and inilib.joy, with features for command-line arguments, I/O redirection, and inclusion of external Joy files.[3][20] Following the university's site changes after 2011, the original materials have been preserved through web archives, such as the Wayback Machine capture from September 2012, and mirrors that maintain the full prototype source.Modern ports
Since the original C implementation of Joy by Manfred von Thun ceased active development around 2003, community-driven ports have emerged to enhance performance, portability, and integration with modern ecosystems. These efforts, primarily hosted on GitHub, focus on reimplementing Joy's core semantics while occasionally adding features like improved input/output handling. One notable port is Joy-cpp, a C++ implementation developed for better performance and cross-platform compatibility. It provides a straightforward interpreter that supports most Joy primitives and tutorial examples, compiling with C++17 on Linux and macOS systems. This port emphasizes efficiency over the original C version, making it suitable for experimentation on contemporary hardware.[21] Joy-hs is a Haskell-based interpreter that aligns Joy's functional purity with Haskell's type system and lazy evaluation. Written entirely in Haskell, it implements Joy's stack-based execution and combinators, serving as an educational tool to explore concatenative programming within a strongly typed environment. The project, last updated in 2014, remains a reference for those interested in functional language implementations.[22] Other ports include JoyJ, a JVM-based interpreter from 2009 that enables Joy programs to run on the Java Virtual Machine, facilitating integration with Java libraries for broader applicability. Stackist, an exploratory Haskell project, offers a fresh implementation of Joy with plans for extensions like a type system and compilation to native code, though it remains in early stages. Joy-js, a JavaScript interpreter, supports browser-based execution and includes a basic REPL, with ongoing work on parsing and combinators. Joypy, a Python dialect from 2014-2018, uses continuation-passing style to closely mimic Joy while adding Pythonic conveniences. These ports generally aim for full compatibility with Joy's semantics, with some incorporating enhancements such as better I/O primitives for practical use.[23][24][25][26] From 2020 to 2025, activity on these ports has been sporadic, driven by small community contributions rather than major releases. Discussions on platforms like Hacker News in 2020 highlighted Joy's enduring appeal for educational purposes and inspired minor updates, but no large-scale projects or official revivals have materialized, keeping the focus on niche, hobbyist maintenance.[27]Programming examples
Introductory examples
Joy, a stack-based functional programming language, can be introduced through simple programs that demonstrate its core mechanics of function composition and stack manipulation. These examples illustrate how literals push values onto the stack, basic operations consume and produce stack elements, and definitions encapsulate reusable code. All examples assume an initially empty stack and use the. operator to print the top stack element and remove it.[5]
To square a number, the program defines a word square that duplicates the top stack element and multiplies it by itself. The definition is DEFINE square == dup * ., where dup duplicates the top item (stack: → [n n]) and * multiplies the top two items (stack: [n n] → [n²]). Executing 5 square proceeds as follows: push 5 (stack: [28]); invoke square, which duplicates to [5 5], multiplies to [29], and prints 25 (stack: []). This results in the output 25 on the console.[5]
Addition of two numbers is straightforward using the built-in + operator, which consumes the top two stack elements and pushes their sum. The program 10 20 + . executes step-by-step: push 10 (stack: [30]); push 20 (stack: [10 20]); apply + (stack: [31]); print 30 (stack: []). The output is 30. Basic words like dup and + manipulate the stack as described in the language's syntax.[5]
Duplication followed by printing showcases stack growth and output. The program 3 dup . . works as: push 3 (stack: [32]); dup yields [3 3]; first . prints 3 and removes it (stack: [32]); second . prints 3 (stack: []). The console shows 3 followed by 3 on a new line. This highlights how dup enables reuse of values without variables.[5]
A simple recursive definition can be built using the primrec combinator, which applies a quoted program iteratively from 0 to n, starting with an initial value. For a factorial stub, define DEFINE fact == [1] [*] primrec ., where [1] provides the base case (1 for n=0), [*] is the step function multiplying by the current index, and primrec performs the recursion. Executing 5 fact pushes 5 (stack: [28]); invokes fact, which applies the combinator to compute 120 (stack: ); and prints 120 (stack: []). The stack trace for primrec involves iterative application: starting with [0 [33] [*]] effectively building up to [5 120], then popping intermediates. This introduces higher-order combinators for defining functions without explicit loops.[5]
Advanced programs
Advanced programs in Joy exploit the language's recursion combinators and higher-order functions to implement sophisticated algorithms with minimal code, highlighting the paradigm's ability to compose operations directly on the stack without explicit parameters or returns. These examples illustrate how Joy's design enables concise expressions of complex logic, such as sorting and recursive computations, while maintaining functional purity.[5] A canonical example of binary recursion is the quicksort algorithm, which partitions a list around a pivot and recursively sorts the sublists. The complete program, defined as a quotation and executed with a period, is:[small] [] [uncons [>] split] [swapd cons concat] binrec .
[small] [] [uncons [>] split] [swapd cons concat] binrec .
binrec combinator, which handles the binary branching pattern: it applies the condition quotation [small] to the input list; if true (list size ≤ 1), it executes the base case [] (identity, leaving the list unchanged in practice via stack manipulation); otherwise, it applies the branching quotation [uncons [>] split] to extract the pivot (first element via uncons), compare subsequent elements to it ([>]), and split into lesser and greater lists; then recurses on each sublist and combines them around the pivot using [swapd cons concat], where swapd repositions the pivot, cons attaches it to the first sorted sublist, and concat joins the results.[29][35]
To trace execution on the small list [3 1 2], start with the stack containing the list. The binrec applies [small], which checks size (3 > 1, false). Then [uncons [>] split] decons the list to pivot 3 and tail [1 2], compares tail elements to 3 (1 < 3, 2 < 3), yielding lesser [1 2] and greater []. Recurse on lesser [1 2] (size 2 > 1; uncons pivot 1 tail [36]; split: 2 > 1 so lesser [] greater [36]; recurse on [] (small, base returns []), [36] (small, base returns [36]); combine: swapd positions 1 between [] and [36], cons to [1 2], concat to [1 2]). Recurse on greater [] (small, base [] but empty). Combine top-level: after recursions, stack has [1 2] 3 []; swapd → [1 2] [] 3, cons → [1 2] [32], concat → [1 2 3]. The stack now holds the sorted [1 2 3]. This trace demonstrates how stack transformations propagate without explicit variable binding.[10]
Mapping a function over a list is achieved with the map combinator, which applies a quoted program to each element and collects results into a new list. For doubling elements in [1 2 3], the code is [1 2 3] [2 *] map, yielding [2 4 6] on the stack. The map iterates the quotation [2 *] on each item (stack: item 2 *, result pushed), building the output list via internal cons operations, preserving the original input list below. This showcases Joy's higher-order capabilities for list processing.[5]
Recursive definitions like factorial utilize the ifte combinator for conditional branching. The program is defined as:
DEFINE fact == [dup 0 =] [pop pop 1] [pop dup 1 - fact *] ifte .
DEFINE fact == [dup 0 =] [pop pop 1] [pop dup 1 - fact *] ifte .
5 fact), ifte pushes the three quotations onto the stack above 5, executes the condition [dup 0 =] leaving [5 false], then in the else branch executes [pop dup 1 - fact *] (pop discards false, leaving [28]; dup to [5 5]; 1 - to [5 4]; recurses fact on 4 yielding 24, stack [5 24]; * to 120). Base case for 0: executes condition leaving [0 true], then [pop pop 1] (pop true and pop 0, push 1 leaving [33]). This linear recursion mirrors mathematical definition, with ifte selecting branches based on the condition atop the stack.[5][35]
Program development in Joy often proceeds by translating pseudo-code directly into stack-manipulating quotations and combinators. For quicksort, the pseudo-code is: if the sequence S has 0 or 1 element, return S; otherwise, take the first element p as pivot, split the rest into L (elements < p) and G (elements ≥ p), return qsort(L) cons p concat qsort(G). The Joy translation mirrors this: condition [small] (true for small S), base [] (identity on S), branching [uncons [>] split] (produces L G with p below via uncons), combining [swapd cons concat] (p sortedL sortedG → sortedL p sortedG via swapd, then cons to sortedL , concat to full). This step-by-step mapping avoids auxiliary variables, relying on stack order. A similar approach applies to developing a simple parser, such as an expression evaluator, where pseudo-code for recursive descent (e.g., parse term, then add/sub terms) translates to nested ifte or linrec over tokenized input lists.[29]
These advanced examples benefit from Joy's stack-oriented efficiency, where operations like dup, swapd, and combinators execute in constant time without function call overhead or garbage collection for temporary structures; recursion leverages the evaluation stack naturally, yielding average O(n log n) for quicksort and O(n) for map and factorial, comparable to imperative implementations but with purer composition.[5]
Legacy and influence
Impact on other languages
Joy's design principles, particularly its concatenative and purely functional approach using quotations and combinators, have directly influenced several subsequent programming languages in the stack-based and functional paradigms. One prominent example is Factor, a dynamic, object-oriented concatenative language developed by Slava Pestov starting in 2003. Factor builds on Joy's stack manipulation and higher-order combinators while incorporating elements from Forth, Lisp, and Self to create a more practical system for general-purpose programming, including extensive libraries for tasks like web development and systems programming.[37] Other languages explicitly inspired by Joy include Cat, V, and Trith, which extend its ideas for specific domains such as scripting and concurrency. Cat, created by Christopher Diggins in 2006, is a statically typed, pure functional stack-based language that adds type inference and targets the .NET platform, emphasizing Joy's point-free style but with formal type safety to address limitations in dynamic stack manipulation.[38] V, a simple dynamic concatenative language similar to Joy and PostScript, focuses on lightweight stack operations for scripting tasks.[39] Trith, developed around 2010, draws from Joy's concatenative model alongside Forth and Factor to support concurrent programming in a Lisp-influenced syntax, aiming for embeddability in larger systems.[31] Joy has also influenced more recent languages like Mlatu, a total functional concatenative language emphasizing correctness and simplicity.[41] Beyond direct descendants, Joy has contributed to broader interest in point-free programming and stack-based functional paradigms, influencing evolutions in concatenative languages derived from Forth by promoting composition over explicit application. Its emphasis on combinators for recursion and higher-order functions has resonated in discussions of functional purity without variables.[10] In academic contexts, Joy has been referenced in post-2003 papers exploring combinatory logic and category-theoretic foundations for functional languages. For instance, works on typing stack-based languages cite Joy as a foundational example of concatenative functional programming, analyzing its semantics for type inference and purity.[42] Similarly, studies on compositional functional programming invoke Joy's model to illustrate stack effects and monoidal structures in category theory applications.[43] Despite these influences, Joy's impact remains niche due to its experimental nature and lack of widespread implementation or tooling, resulting in no mainstream adoption and limited visibility outside specialized concatenative programming communities.[42]Community and resources
The Joy programming language maintains a small, niche community primarily composed of enthusiasts and academics interested in concatenative and functional paradigms. Activity centers around open-source repositories on GitHub, where implementations like joy-cpp and Joypy attract occasional contributions and forks from developers exploring stack-based languages.[21][26] Discussions persist in online forums, such as a 2020 Hacker News thread that garnered interest in Joy's point-free style and its potential for educational use.[27] The community remains modest in scale, with no large-scale conferences or user groups, but it benefits from documentation on the Esolang wiki, which covers Joy's core features.[44] Key learning resources originate from Joy's creator, Manfred von Thun, whose tutorials—such as the "An Informal Tutorial on Joy" presented at EuroForth 2001—provide foundational explanations of the language's stack operations and function composition.[5] These materials, revised around 2003 and now archived, remain accessible via mirrors like the one maintained by Kevin Albrecht, which includes practical examples of Joy programs alongside libraries for sequences and numerics.[7][3] Albrecht's site also hosts supplementary papers, such as "Some Simple Programming in Joy," demonstrating translations from pseudo-code to Joy equivalents.[29] For modern access, GitHub hosts several interpreter implementations, including JavaScript-based joy-js and Haskell's joy-hs, enabling experimentation without extensive setup.[25][6] Recent additions include the Joy of Postfix, a Kotlin-based Android app for Reverse-Polish-Notation enthusiasts, providing an interactive way to explore Joy-like concepts.[45] There are no dedicated forums for Joy, and Stack Overflow features only sparse tags related to concatenative languages, with questions often bundled under broader topics like Forth. A typical learning path begins with von Thun's tutorials for basics, progressing to Albrecht's libraries—such as seqlib.joy for handling ordered lists and strings—before tackling custom programs.[3] However, active support is limited, posing challenges for newcomers seeking real-time help. Documentation gaps persist due to the age of primary resources, with most materials predating 2011 and lacking updates for contemporary computing environments. This has led to potential revival interest within esoteric programming (esolangs) communities and educational contexts, where Joy's simplicity aids in teaching functional concepts, though broader adoption remains hindered by the absence of maintained forums or recent publications.[44]References
- Joy differs from lambda calculus languages in that it has no variables and hence no abstraction. It differs from the combinatory calculus in that it does not ...
- By Manfred von Thun. Introduction. This paper shows how to write simple programs in Joy. Since Joy is very different from familiar programming languages, ...
- Language-Oriented Programming (LOP) we argue that the yet relatively unknown paradigm of concatena- tive programming is valuable for fundamental software ...
- Trith is inspired and influenced by experience with Forth, Lisp and Scheme in general, and the concatenative languages Joy, XY, Factor and Cat in particular.
- MvT: The language Joy is a purely functional programming language. Whereas all other functional programming languages are based on the application of ...
- Joy is a functional programming language which is not based on the application of functions to arguments but on the composition of functions.
- Joy is a programming language based on the composition of functions ... © La Trobe University 2009.
