Module C2.07
From LINQ to seq pipelines
The most transferable C# skill, respelled — same operators, same laziness, a pipe instead of a chain.
LINQ is the part of C# that already thinks functionally: a query is a chain of pure transforms over a
sequence, evaluated only when read. F# carries that idea as the language's default. The .NET type
IEnumerable<T> is F#'s seq<'T> — the same
interface — and every LINQ operator has a one-to-one Seq. sibling:
Where is Seq.filter, Select is
Seq.map, Aggregate is Seq.fold,
GroupBy is Seq.groupBy. Both are lazy and deferred — nothing runs
until enumeration. This is the module where a C# developer is most at home, and it teaches the F# spellings, the
|> pipeline shape, and query { } for the query-syntax fans.
Same query, two spellings
A LINQ method chain reads off its receiver: data.Where(…).Select(…).ToList(), each call
hanging off the value the last one returned. An F# pipeline reads the other way — the subject leads and
each operator takes it as its last argument: data |> Seq.filter … |> Seq.map … |>
Seq.toList. The operations are identical and run in the same order; the difference is the
|> operator threading the value through, top to bottom. The three dives below take
the parts a C# developer cares about: the operator map, the laziness both share, and the query-syntax form.
The first dive walks the operator table — Where→Seq.filter,
Select→Seq.map, Aggregate→Seq.fold, and the rest — worked the
same example in both languages. The second is deferred execution: LINQ does no work until you
enumerate, and seq is the same lazy, pull-based sequence, composable even over the
infinite. The third is query expressions — F#'s query { } for the LINQ
query-syntax fan, and where it earns its place over the |> pipeline.
The three dives · select one
Every LINQ operator has a one-to-one Seq. sibling: Where→Seq.filter, Select→Seq.map, Aggregate→Seq.fold, GroupBy→Seq.groupBy. Same operations, read top-down through a |> pipe.
Open this dive · or pick another above
The query and the pipeline are the same idea spelled two ways. A C# developer already knows what each operator does; this module supplies the F# name and the pipe that carries the value.
The chain, beside the pipe
The shift is concrete. The C# the reader knows filters even numbers, squares them, and materialises the result
with ToList. The F# does the same three steps, but the data leads and
|> threads it through each operator. The result is identical, and both echoes below
were produced — the F# by dotnet fsi, the C# by dotnet-script.
C# — a method chain off the receiver
var result = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } .Where(x => x % 2 == 0) .Select(x => x * x) .ToList(); // [4, 16, 36, 64, 100]
F# — a pipeline; the subject leads
let result = seq { 1..10 } |> Seq.filter (fun x -> x % 2 = 0) |> Seq.map (fun x -> x * x) |> Seq.toList // val result : int list = [4; 16; 36; 64; 100]
C# habit
A LINQ method chain hung off the receiver — data.Where(…).Select(…) — over IEnumerable<T>, deferred until ToList.
F# form
A |> pipeline — data |> Seq.filter … |> Seq.map … — over the same seq<'T>, deferred until Seq.toList.
The three dives
Read them in order. Each stands on valid F# beside the idiomatic C# it mirrors: every
// val it : echo below was produced by dotnet fsi, and every C#
line compiles. Together they take a C# developer from a LINQ method chain to an F# seq
pipeline — the operator names, the laziness both languages share, and the query { } form
for the query-syntax fans.
LINQ operators → Seq operators
The operator map, worked: Where→Seq.filter, Select→Seq.map, Aggregate→Seq.fold, and the rest.
- the mapthe chain becomes a pipe; same operations, read top-down
Deferred execution & laziness
LINQ does no work until you enumerate; seq is the same — lazy, pull-based, composable over the infinite.
- the lazinessan infinite seq taken finitely, plus the re-enumeration trap
Query expressions
For the query-syntax fan, F# has query { } — a computation expression, with from→for…in.
- query { }the keyword map, and when it earns its place over a pipe
References
Sources
- Microsoft — F# documentation — sequences, the
Seqmodule, and query expressions. - FSharp.Core API reference — the
Seqmodule:filter,map,fold,groupBy. - Microsoft — .NET documentation — LINQ,
IEnumerable<T>, and deferred execution on the CLR.
Related in this course
- C2.07.1 — LINQ operators → Seq operators — the operator map, worked.
- C1.07 — Lists, arrays & the lazy seq —
seqand the transform operators in language depth. - C2 — F# for C# developers — the chapter that translates each C# habit into its idiomatic F# form.