Summary#
A lambda is an inline function — x => x.Total > 10m. Where it is passed decides its parameter types: in
orders.Where(o => …) the o is an Order because Where over orders passes one. You may also write the types
yourself, as in C#: (Order o) => o.Total. A declared type is then checked against the one the position passes,
and where nothing passes one — var twice = (int x) => x * 2; — the declared types are what type the lambda.
Signature#
x => body // one parameter, typed by where the lambda is used
(a, b) => body // several parameters
(int a, string b) => body // EXPLICITLY typed — all of them, or none
() => body // no parameters
x => { statements } // a block body, wherever the lambda runs (not a query over stored rows)Description#
Where do a lambda's parameter types come from?#
From the position it is written in — its TARGET. A LINQ clause passes the element (xs.Where(x => …) over a
List<int> binds x as int), a Func<T, R> slot passes its Ts, and an index-aware Select((x, i) => …) passes
the element and its int position. That is why you rarely need to write a type at all.
enum Pace { Slow, Fast }
entity Delivery { [Required] decimal Weight; Pace Pace; }
decimal Heavy() { return Delivery.Where(d => d.Weight > 20m).Sum(d => d.Weight); }
List<int> Positions(List<int> xs) { return xs.Select((x, i) => x * i).ToList(); }Can I write the parameter types myself?#
Yes — exactly as in C#. Write each parameter as Type name, and give every parameter a type or none of them:
int FastOnes() {
return Delivery.Where((Delivery d) => d.Pace == Pace.Fast)
.Select((Delivery d) => d.Pace)
.Where((Pace p) => p == Pace.Fast)
.Count();
}
int Longest(List<string> words) { return words.Select((string w, int i) => w.Length + i).Max() ?? 0; }
int Combine() { Func<int, string, int> f = (int n, string s) => n + s.Length; return f(1, "ab"); }A typed parameter reads the same as an untyped one inside the body; the type is a statement about it, and the compiler holds you to it.
What if the type I write is not the one the position passes?#
It is a compile error naming both, as in C# (CS1678). A declared type is not a conversion: (long x) => over a
List<int> is refused rather than widened, and (int t) => over a sequence of decimal is refused rather than
truncated. Nullability counts for a value type — int? is not int.
the lambda declares `x` as `long`, but its target supplies it, so `x` is `int` — a lambda's declared parameter
types must match its target exactly (C#'s CS1678). Write `(int x) => …`, or leave the types off and let the target
supply them: `(x) => …`.A list that types some parameters and not others — (int x, i) => … — is refused too (C#'s CS0748), with both
spellings that work.
Can a lambda stand on its own, with no target?#
Only when its parameters are typed. var twice = (int x) => x * 2; has everything it needs: the declared int types
x, and the body's type is what it returns, so twice is a Func<int, int> and twice(3) is an int — C#'s
natural type for a lambda. An UNTYPED lambda there has nothing to bind its parameters from and is refused (C#'s
CS0815); give it a type, or declare the variable as a Func<…>.
int Twice(int n) {
var twice = (int x) => x * 2; // Func<int, int>, from the declared type
Func<int, int> alsoTwice = x => x * 2; // the same, typed by the variable instead
return twice(n) + alsoTwice(n);
}Can a lambda have a block body?#
Yes — x => { …; return y; }, exactly as in C#, wherever the lambda RUNS: a Func/Action held in a variable or
passed to a parameter, and a LINQ verb over a list already in memory. The block holds anything a function body can —
locals, loops, if, try/catch, calls — and its return answers the lambda. A Func lambda must return a value
on every path (C#'s CS1643); an Action lambda's return carries none (CS8030).
[Test]
void Block_Lambdas_Run_Their_Statements() {
Func<int, int> fold = x => { var t = x * 2; while (t > 10) { t -= 10; } return t; };
int calls = 0;
Action note = () => { calls++; };
var lengths = new List<string> { "kite", "ox", "harbour" }.Select(w => {
note();
if (w.Length > 4) { return w.Length * 10; }
return w.Length;
}).ToList();
Assert.Equal(16, fold(4) + fold(9));
Assert.Equal(76, lengths.Sum());
Assert.Equal(3, calls);
}Two places take an expression body only. A lambda that becomes part of a QUERY over stored rows is translated to
SQL, and a block has no SQL form — read the rows first (.ToList()) and run the block over the list, or move the
statements into a function. And a render expression is evaluated where no call can run, so a lambda invoked there
keeps an expression body. A block lambda also cannot wait inside itself for the other side or a durable timer:
compute what it waits for before it runs.
Can a lambda change a variable of the code around it?#
Yes, while it runs in the function that declared it — the lambda and the function share the variable, as in C#. Hold the lambda in a local and call it:
[Test]
void A_Lambda_Shares_What_It_Writes() {
int count = 0;
Action bump = () => count++;
bump();
bump();
Assert.Equal(2, count);
}What it may not do is LEAVE that function while it writes such a variable — passed to another function, stored in a field or a list, returned, or not held in a local at all. It would then run where the variable is not, so that is a compile error that says so. Have such a lambda return the new value instead, and assign it where the variable lives.
Does a lambda see a variable that changes after it is made?#
Yes — a lambda captures the VARIABLE, not its value, so it reads what the variable holds when the lambda RUNS, as in
C#. That includes a loop variable a for moves on:
[Test]
void A_Lambda_Reads_The_Current_Value() {
int step = 1;
Func<int> scaled = () => step * 10;
step = 5;
Assert.Equal(50, scaled());
int total = 0;
for (int i = 1; i <= 3; i++) {
Func<int> tenTimes = () => i * 10;
total += tenTimes();
}
Assert.Equal(60, total);
}The same rule as a lambda that writes: it holds while the lambda runs in the function that declared it. One that is
handed on or stored, and reads a variable the function changes afterwards, is refused by name — pass the value to it
as a parameter instead. A lambda passed straight to a call (Apply(() => k + 1)) is run by that call before anything
around it can change, so it reads the same value either way.
Examples#
decimal Weights(List<decimal> ws) {
var light = ws.Where(w => w < 5m).Sum(); // typed by the target
var heavy = ws.Where((decimal w) => w >= 5m).Sum(); // typed by you — and checked
return light + heavy;
}See also#
- Func<T, R> — a
Func<T, R>parameter or field that takes a lambda and is invoked for its value - Querying data — LINQ over your data, where most lambdas are written
- var —
var, and what a declaration takes its type from - Typed locals — declaring a local with an explicit type