liveBook · Manning

Bind

As explained in Fabulous Adventures in Data Structures and Algorithms

Bind is an operation that chains a context-producing function to a contextual value while flattening the resulting nested context.

Suppose a lazy value holds an integer, and the next function returns a lazy result. Mapping that function produces `Lazy<Lazy<R>>`: two layers where the workflow needs one. Bind applies a context-producing function and returns a single contextual result instead. section 11.3

Its shape is `Bind(M<T> value, Func<T, M<R>> function) → M<R>`. The result keeps the capability supplied by `M` while preserving what the function does. For nullable values, Bind skips the function when no value exists; for lazy values, it delays the work; for tasks, it awaits both stages; for sequences, it concatenates the sequences produced for each item. section 11.3

static Nullable<R> Bind<T, R>(Nullable<T> x, Func<T, Nullable<R>> func)
    where T : struct, R : struct =>
    x.HasValue ? func(x.Value) : null;

static Lazy<R> Bind<T, R>(Lazy<T> x, Func<T, Lazy<R>> func) =>
    new Lazy<R>(() => func(x.Value).Value);

async static Task<R> Bind<T, R>(Task<T> x, Func<T, Task<R>> func) =>
    await func(await x);

static IEnumerable<R> Bind<T, R>(IEnumerable<T> x, 
    Func<T, IEnumerable<R>> func) 
{
    foreach(T t in x)
        foreach(R r in func(t))
            yield return r;
}

Compare the four implementations: each handles its own context, so callers can chain context-producing functions without handling a nested wrapper. section 11.3

For sequences, Bind is called `SelectMany` in C# and `flatMap` in Python and Java. Unlike `Select` (Map), it takes a function that returns a sequence for each input and yields their elements as one sequence. This also makes filtering expressible through Bind: produce a one-item sequence when a predicate succeeds, or an empty one when it fails. That version works, but allocates many tiny sequences. section 11.4

Binding a likelihood to a prior

A discrete distribution is another context. Bind treats its input as a prior distribution and the supplied function as a likelihood, then forms a joint distribution and projects its marginal. With a 10% prior probability of a cold and different sneezing probabilities depending on whether a cold is present, `prior.Bind(SneezedGivenCold)` gives sneezing weights of 113 for yes and 887 for no. section 14.1

Bind and Unit, which wraps a plain value, are enough to derive two related operations: `Map(x, f) = Bind(x, t => Unit(f(t)))` applies a plain-value function, while `Join(x) = Bind(x, inner => inner)` removes one wrapper layer. For distributions, Join can flatten a coin-flip choice between two dice distributions into one distribution of die results. section 11.3 section 14.1

How Fabulous Adventures in Data Structures and Algorithms covers it

How deep it goes
Defines the generic signature and works through nullable, lazy, task, sequence, and discrete-distribution implementations.
Examples
Uses nested lazy values, sequence flattening, and a cold-and-sneezing probability calculation.
What it assumes
Assumes familiarity with C# generic types and functions while defining the monad operations used.
What you can do after
Build it from scratch for contextual workflows, or apply it via C# SelectMany for sequences.
How it connects
Connects monads, Unit, Map, Join, SelectMany, filtering, and probability distributions.

Also explained in

Compare how 5 books explain bind →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Fabulous Adventures in Data Structures and Algorithms. The section links point to the parts of the book it draws on.