Functions, Currying & Partial Application
Relevant source files
The following files were used as context for generating this wiki page:
In the Jazz programming language, functions are treated as first-class citizens, meaning they can be passed as arguments, returned from other functions, and assigned to variables. The language adopts a functional paradigm inspired by Haskell and Elixir, implementing strong static typing and type inference for its core expression subset.
A defining characteristic of Jazz is that functions are curried by default. This design choice enables seamless partial application, allowing developers to create new functions by providing only a subset of a function's required arguments. This mechanism is central to the language's approach to code reuse and modularity.
Sources: README.md:14-19, README.md:52-53
Functional Architecture
The Jazz compiler, specifically within the jazz-next/ implementation path, handles function definitions through a pipeline involving parsing, lowering, and semantic analysis. The system differentiates between pure functions and impure functions (denoted with a !), enforcing strict call boundaries where impure functions cannot be invoked from pure contexts.
Purity and Lambdas
Functions in Jazz are pure by default and are typically declared by assigning a lambda to an immutable binding. Multi-line functions utilize curly braces to define their bodies.
// Pure function assignment
isEven = \(i) -> mod(i, 2) == 0.
// Impure function assignment
greet! = \(name) -> {
println! "Hello, ${name}".
}.
Sources: README.md:92-105, jazz-next/README.md:16-25
Function Application Logic
Jazz provides specialized syntax for function application to maintain readability. The $ operator is a right-associative, zero-precedence operator used to apply functions without excessive parentheses.
The following diagram illustrates the flow from a function declaration to its application through the compiler's semantic stages:
The diagram shows the transition from raw source to runtime, highlighting the purity enforcement checked during the analysis phase.
Sources: README.md:65-68, jazz-next/README.md:21-27, jazz-next/scripts/test-warning-config.sh:42
Currying by Default
Every function in Jazz is conceptually a series of nested single-argument functions. When a function is defined with multiple parameters, the compiler automatically curries it. This allows a function like (+) :: Num -> Num -> Num to be viewed as a function that takes one Num and returns another function.
Partial Application Examples
Partial application is achieved by omitting trailing arguments. This is commonly used with higher-order functions like map or filter.
| Feature | Syntax Example | Description |
|---|---|---|
| Partial Application | add10 = (+10) | Creates a new function Int -> Int by fixing one argument of the addition operator. |
| Point-free Style | add10List = map add10 | Defines a list transformation without explicitly naming the list variable. |
| Placeholder Syntax | map (\Num.pow(2, \0)) myArr | A planned feature for shorthand partial application using positional placeholders. |
Sources: README.md:55-63, README.md:85-88
Compiler and Type System Integration
The jazz-next compiler infrastructure provides specific modules to handle the lifecycle of functions and their application.
Key Implementation Components
The following modules are responsible for the functional semantics within jazz-next/:
Parser.hs: Handles the surface syntax for lambdas (\args -> body) and function calls.Analyzer.hs: Performs binding/signature coherence checks and enforces purity rules (preventing!calls in pure bodies).TypeInference.hs: Plumbs types through curried applications and ensures signature adjacency.LambdaSemanticsSpec.hs: A test suite specifically covering the semantic behavior of lambdas and function application.
Sources: jazz-next/README.md:16-28, jazz-next/scripts/test-warning-config.sh:30-45
Sequence of Partial Application
The following sequence diagram represents how the system resolves a partially applied function during analysis:
This diagram illustrates the internal coordination between compiler phases to resolve the types of curried functions.
Sources: jazz-next/README.md:16-28, jazz-next/scripts/test-warning-config.sh:44
Summary
Functions, Currying, and Partial Application form the core of the Jazz programming experience. By making functions curried by default, the language simplifies higher-order programming and enables a concise, point-free style of development. The jazz-next compiler enforces these rules through a specialized analyzer and type inference system that protects pure code from side effects while maintaining the flexibility of first-class functions.
Sources: README.md:14-19, AGENTS.md:11-13