Skip to main content

.NET F# Introduction

What is F#?

F# (pronounced "F sharp") is a mature, open-source, cross-platform programming language in the .NET family. It was developed by Microsoft Research and is now maintained by the F# Software Foundation and Microsoft. F# is a functional-first language, which means it encourages a style of programming that treats computation as the evaluation of mathematical functions.

Key characteristics of F#:

  • Concise syntax that reduces code complexity
  • Strong type system that helps prevent many common bugs
  • First-class functions that can be passed as values
  • Immutable values by default, reducing side effects
  • Pattern matching for elegant handling of data structures
  • Full integration with the .NET ecosystem

Why Learn F#?

Learning F# offers several advantages:

  1. Cleaner code - F# programs typically require fewer lines of code than equivalent C# or VB.NET programs
  2. Fewer bugs - The type system and immutability help prevent entire classes of errors
  3. Increased productivity - Features like type inference reduce boilerplate code
  4. Seamless .NET interoperability - Use any .NET library directly from F#
  5. Data science and AI - Excellent for working with data and implementing machine learning algorithms

Setting Up F#

To get started with F#, you'll need the .NET SDK installed on your system. You can download it from dotnet.microsoft.com.

Once installed, you can create a new F# project:

bash
dotnet new console -lang F# -o MyFirstFSharpApp
cd MyFirstFSharpApp

This creates a new F# console application. Let's look at the default program file (Program.fs):

fsharp
// Learn more about F# at http://docs.microsoft.com/dotnet/fsharp

open System

// Define a function to construct a message to print
let from whom =
sprintf "from %s" whom

[<EntryPoint>]
let main argv =
let message = from "F#" // Call the function
printfn "Hello world %s" message
0 // return an integer exit code

To run this program:

bash
dotnet run

Output:

Hello world from F#

Basic F# Syntax

Variables and Functions

In F#, you define values and functions using the let keyword:

fsharp
// Define a value
let greeting = "Hello, world!"

// Define a function
let add x y = x + y

// Using the function
let sum = add 5 3
printfn "Sum: %d" sum

Output:

Sum: 8

Notice that:

  • F# uses type inference, so you don't need to declare types explicitly
  • Function parameters are separated by spaces, not commas
  • Parentheses are often optional

Immutability

By default, values in F# are immutable, which means they cannot be changed after they're defined:

fsharp
let name = "John"
// name <- "Jane" // This would cause a compilation error

To create mutable values, you use the mutable keyword:

fsharp
let mutable count = 0
count <- count + 1 // This is valid
printfn "Count: %d" count

Output:

Count: 1

Collections

F# has several built-in collection types:

fsharp
// Lists
let fruits = ["Apple"; "Banana"; "Orange"]

// Arrays
let numbers = [| 1; 2; 3; 4; 5 |]

// Sequences
let evens = seq { 2..2..10 }

// Tuples
let person = ("John", 30)

// Accessing elements
printfn "First fruit: %s" fruits[0]
printfn "Second number: %d" numbers[1]
printfn "Person name: %s, age: %d" (fst person) (snd person)

Output:

First fruit: Apple
Second number: 2
Person name: John, age: 30

Pattern Matching

Pattern matching is a powerful feature in F# that allows you to match values against patterns and execute code based on the match:

fsharp
let describeNumber n =
match n with
| 0 -> "Zero"
| 1 -> "One"
| 2 -> "Two"
| n when n < 0 -> "Negative"
| _ -> "Many" // Default case

[1..3] |> List.map describeNumber |> List.iter (printfn "%s")
printfn "%s" (describeNumber -5)

Output:

One
Two
Many
Negative

Piping and Function Composition

F# uses the |> operator (pipe) to chain function calls, making code more readable:

fsharp
// Without piping
let result1 = List.sum (List.filter (fun x -> x % 2 = 0) [1..10])

// With piping
let result2 =
[1..10]
|> List.filter (fun x -> x % 2 = 0)
|> List.sum

printfn "Sum of even numbers: %d" result2

Output:

Sum of even numbers: 30

Real-World Example: Data Processing

Let's create a more practical example that processes a list of orders:

fsharp
// Define types for our domain
type Product = {
Name: string
Price: decimal
}

type Order = {
OrderId: int
Products: Product list
}

// Sample data
let products = [
{ Name = "Laptop"; Price = 999.99m }
{ Name = "Mouse"; Price = 25.50m }
{ Name = "Keyboard"; Price = 45.75m }
{ Name = "Monitor"; Price = 159.99m }
]

let orders = [
{ OrderId = 1; Products = [products[0]; products[1]] }
{ OrderId = 2; Products = [products[2]] }
{ OrderId = 3; Products = [products[0]; products[3]; products[1]] }
]

// Calculate total price for each order
let calculateOrderTotal (order: Order) =
order.Products
|> List.sumBy (fun p -> p.Price)

// Find orders above a certain amount
let findExpensiveOrders threshold =
orders
|> List.filter (fun o -> (calculateOrderTotal o) > threshold)
|> List.map (fun o -> o.OrderId)

// Print results
orders
|> List.iter (fun o ->
printfn "Order #%d total: $%.2f" o.OrderId (calculateOrderTotal o))

let expensiveOrders = findExpensiveOrders 200.0m
printfn "\nExpensive orders (>$200): %A" expensiveOrders

Output:

Order #1 total: $1025.49
Order #2 total: $45.75
Order #3 total: $1185.48

Expensive orders (>$200): [1; 3]

F# and Object-Oriented Programming

While F# is functional-first, it fully supports object-oriented programming as well:

fsharp
// Define a class
type Person(name: string, age: int) =
// Properties
member this.Name = name
member this.Age = age

// Method
member this.Greeting() =
sprintf "Hi, I'm %s and I'm %d years old." name age

// Static method
static member CreateAdult(name) = Person(name, 18)

// Create instances
let john = Person("John", 30)
let adult = Person.CreateAdult("Jane")

// Use the instances
printfn "%s" (john.Greeting())
printfn "%s" (adult.Greeting())

Output:

Hi, I'm John and I'm 30 years old.
Hi, I'm Jane and I'm 18 years old.

Asynchronous Programming

F# has excellent support for asynchronous programming with its async workflow:

fsharp
// Simulate an asynchronous operation
let fetchDataAsync url =
async {
printfn "Fetching data from %s..." url
do! Async.Sleep 1000 // Simulate network delay
return sprintf "Data from %s" url
}

// Execute async operations in parallel
let fetchAllData() =
let urls = ["https://example.com/api/users"; "https://example.com/api/products"]
urls
|> List.map fetchDataAsync
|> Async.Parallel
|> Async.RunSynchronously

// Run the example
let results = fetchAllData()
Array.iter (printfn "%s") results

Output:

Fetching data from https://example.com/api/users...
Fetching data from https://example.com/api/products...
Data from https://example.com/api/users
Data from https://example.com/api/products

Summary

F# is a versatile, practical language that combines the efficiency and clarity of functional programming with the extensive capabilities of the .NET ecosystem. Key takeaways include:

  • F# is a functional-first language that also supports object-oriented and imperative programming
  • It features concise syntax, strong typing, and immutable values by default
  • Pattern matching and piping make code more readable and maintainable
  • F# works seamlessly with other .NET languages and libraries
  • It excels in data processing, parallel programming, and domain modeling scenarios

Additional Resources

Here are some resources to continue your F# journey:

Exercises

  1. Basic Functions: Create a function that calculates the factorial of a number.
  2. Collections: Write a program that filters a list of numbers to find all prime numbers.
  3. Pattern Matching: Implement a simple calculator that takes two numbers and an operator (as a string) and returns the result.
  4. Data Processing: Create a program that reads a text file, counts word frequencies, and displays the top 5 most common words.
  5. Object-Oriented: Design a simple banking system with classes for accounts, transactions, and customers.

Happy coding with F#!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)