Functional programming basics
WebPPL is a functional subset of JavaScript
WebPPL looks like JavaScript, but you should treat it as a functional subset of JavaScript: most idiomatic WebPPL programs are expression-oriented and built by composing functions.
This is not only a matter of style. WebPPL supports probabilistic inference by transforming your program internally (for example into continuation-passing style). Those transformations work best—and most predictably—when your code is written in a functional way.
In practice, for probabilistic modeling in WebPPL, the functional style is the only reliable style.
What “functional programming” means here
Functional programming (FP) is a way of writing programs by building and composing functions, rather than by repeatedly mutating variables and data structures.
A practical checklist for WebPPL:
Prefer expressions over statements. Write code that returns values instead of code that updates state.
Prefer pure functions. A pure function always returns the same result for the same inputs. (Randomness is a deliberate exception in probabilistic programs.)
Avoid mutation. Instead of changing an array/object in-place, create a new one.
Use higher-order functions. Functions like
map,reduce, andrepeatare central tools.
A useful mental model
In FP, a program is a pipeline:
Generate values (often by sampling).
Transform them (with functions like
map).Summarize them (with
reduceor a custom aggregator).Return the result.
This is exactly how many WebPPL models are structured.
Key terms (short definitions)
- Expression
A piece of code that evaluates to a value (e.g.
1 + 2,flip(0.7), or a function call).- Pure function
A function with no side effects and deterministic behavior: same inputs -> same output.
- Higher-order function
A function that takes a function as an argument or returns a function (e.g.
map(f, xs)or a function that returns another function).- Closure
A function that “remembers” variables from the scope where it was created.
- Immutability
The practice of not modifying data in-place. Instead, create new values.
Why this matters in probabilistic programming
Probabilistic models are naturally described by composition:
a prior is a function that produces uncertain values,
a likelihood scores or observes data given those values,
inference turns the model into a posterior distribution.
This “functions all the way down” structure is exactly what FP encourages.
Common pitfalls (and the FP-friendly alternative)
- Mutation-heavy code
Avoid patterns like repeatedly pushing into arrays or updating objects in-place. Prefer building a new list with
map/repeatand returning it.- Hidden state
Avoid relying on variables that change across time in complex ways. Prefer passing values explicitly through function arguments.
- Deep imperative control flow
Prefer small helper functions and expression-oriented conditionals.
Executable micro-example: closures
This example demonstrates a closure: a function that remembers variables from
its environment, and how that plays nicely with map.
// A closure: a function that "remembers" variables from its environment.
var makeAdder = function(a) {
// The returned function closes over (remembers) 'a'.
return function(x) {
return a + x;
};
};
var add10 = makeAdder(10);
display("add10(5) =");
display(add10(5));
display("map(add10, [1,2,3]) =");
display(map(add10, [1, 2, 3]));
// Another closure example: parameterized threshold check.
var makeThreshold = function(t) {
return function(x) { return x < t; };
};
var below3 = makeThreshold(3);
display("map(below3, [1,2,3,4,5]) =");
display(map(below3, [1, 2, 3, 4, 5]));
// WebPPL-flavored closure: a function that returns a stochastic function.
var makeBiasedCoin = function(p) {
return function() { return flip(p); };
};
var coin70 = makeBiasedCoin(0.7);
display("repeat(10, coin70) =");
display(repeat(10, coin70));
node:fs:448
return binding.readFileUtf8(path, stringToFlags(options.flag));
^
Error: ENOENT: no such file or directory, open '../examples/functional/closures.wppl'
at Object.readFileSync (node:fs:448:20)
at main (/home/runner/work/WebpplHelp/WebpplHelp/node_modules/webppl/webppl:121:17)
at Object.<anonymous> (/home/runner/work/WebpplHelp/WebpplHelp/node_modules/webppl/webppl:161:1)
at Module._compile (node:internal/modules/cjs/loader:1521:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1623:10)
at Module.load (node:internal/modules/cjs/loader:1266:32)
at Module._load (node:internal/modules/cjs/loader:1091:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:164:12)
at node:internal/main/run_main_module:28:49 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '../examples/functional/closures.wppl'
}
Node.js v20.20.1
Where to go next
If you are new to functions in WebPPL, continue with functions_and_closures.
For the most common list-processing patterns, see map_filter_reduce.
For WebPPL-specific quirks, see gotchas_js_webppl.