jonnifyfsharp

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

C2.07.1 LINQ → Seq operators C2.07.2 deferred execution C2.07.3 query expressions the map the laziness query { }
C2.07.1 · LINQ operators → Seq operators

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.

You learn the operator map, worked the same example in C# and F#

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.

live module hub built dive ready soon specified

References

Sources

Related in this course