So, you've made it past the basics of F# and are hungry for more. Congratulations—you’ve entered the intermediate zone, where the code gets smarter, collections come alive, pattern matching makes you feel like a wizard, and modules bring order to the chaos. And yes, we’re still writing code that makes your C-style language friends scratch their heads and mutter, "Wait, where are the curly braces?"
Let’s dive deeper into these concepts while keeping things as lighthearted as the syntax itself.
Collections: Data With a Dash of Elegance
Collections in F# are like the Swiss Army knives of data—they're versatile, efficient, and make you look cool when you use them. Today, we’ll work with lists, arrays, and sequences. These are the bread, butter, and jam of F# programming.
Mapping and Filtering: Using Inline Functions Like a Pro
Inline functions are little snippets of logic you can sneak into List.map
or List.filter
without going through the hassle of naming them. Why name something you’ll use just once? Here’s how they work:
let numbers = [1; 2; 3; 4; 5]
let doubled = List.map (fun x -> x * 2) numbers
let evens = List.filter (fun x -> x % 2 = 0) numbers
printfn "Original: %A" numbers
printfn "Doubled: %A" doubled
printfn "Evens: %A" evens
List.map
takes your collection and applies a transformation.
Think of it as giving each list item a glow-up.List.filter
lets you kick out the unworthy items.
Kind of like a VIP line for your data.
Results:
Original: [1; 2; 3; 4; 5]
Doubled: [2; 4; 6; 8; 10]
Evens: [2; 4]
Here’s the fun part: You don’t need to define a separate function! Using fun x ->
inline makes your code shorter and, dare I say, elegant.
Bonus: Pipelining Makes It Magical
Ever wanted your code to read like a sentence? F#’s pipeline operator (|>
) has you covered.
let result =
[1; 2; 3; 4; 5]
|> List.filter (fun x -> x % 2 = 0) // Even numbers only
|> List.map (fun x -> x * x) // Square them
printfn "Result: %A" result
Output:
Result: [4; 16]
It’s like the assembly line for your data: filter, transform, done.
Pattern Matching: The Choose-Your-Own-Adventure of Code**
If you’ve ever played a video game where your choices affect the ending (insert RPG of choice here), you’ll love pattern matching. It’s like switch
statements in other languages, but on steroids, with less syntax clutter.
Simple Matching
Here’s a function to determine if a number is odd or even:
let describeNumber x =
match x with
| x when x % 2 = 0 -> "Even"
| _ -> "Odd"
printfn "5 is %s" (describeNumber 5)
printfn "10 is %s" (describeNumber 10)
Results:
5 is Odd
10 is Even
The match
keyword takes care of branching. No need for if...else
sprawl. It’s clean and readable—like a perfectly aligned bookshelf.
Deep Matching
Want to get fancy? Let’s match against complex data structures:
type Shape =
| Circle of float
| Rectangle of float * float
let area shape =
match shape with
| Circle r -> System.Math.PI * r * r
| Rectangle (w, h) -> w * h
printfn "Area of Circle (r=2): %f" (area (Circle 2.0))
printfn "Area of Rectangle (w=3, h=4): %f" (area (Rectangle (3.0, 4.0)))
Results:
Area of Circle (r=2): 12.566371
Area of Rectangle (w=3, h=4): 12.000000
Modules: Marie Kondo Your Code
By now, your project might be growing, and it’s time to tidy up. Modules let you organize code into logical sections without turning your file into a sprawling mess.
Here’s an example:
module MathOperations =
let add x y = x + y
let subtract x y = x - y
let multiply x y = x * y
let result = MathOperations.add 3 5
printfn "3 + 5 = %d" result
Modules act like containers. You get to group related functions and access them with dot notation. Suddenly, your code is organized and impressively professional.
4. The Great Inline Function Debate
Now that we’ve seen fun x ->
, let’s clear up why it’s so cool:
- Direct Logic: Inline functions make code clear right where it’s used.
- Avoids Naming Things: Because naming is hard.
- Perfect for Pipelines: Combined with
|>
, they create a flow that’s readable and functional.
However, don’t overdo it. If your inline function is longer than a tweet, give it a name and some space to breathe.
Let’s Bring It All Together
Here’s a final example combining collections, pattern matching, and modules:
module ShapeCalculator =
type Shape =
| Circle of float
| Rectangle of float * float
let area shape =
match shape with
| Circle r -> System.Math.PI * r * r
| Rectangle (w, h) -> w * h
let describeAreas shapes =
shapes
|> List.map (fun shape -> sprintf "Area: %f" (area shape))
And to test it:
let shapes = [ShapeCalculator.Circle 3.0; ShapeCalculator.Rectangle (2.0, 5.0)]
let descriptions = ShapeCalculator.describeAreas shapes
List.iter (printfn "%s") descriptions
Output:
Area: 28.274334
Area: 10.000000
Conclusion
Congratulations! You’ve tackled some of F#’s intermediate features, like collections, pattern matching, and modules. With these tools, your programs are cleaner, smarter, and just plain fun to write. Plus, you can officially brag to your programmer friends that you’ve embraced functional programming.
What’s next? Advanced concepts—because you’re clearly unstoppable. Onward, to recursive functions, computation expressions, and making the JVM folks jealous!