> ## Documentation Index
> Fetch the complete documentation index at: https://docs.matterai.so/llms.txt
> Use this file to discover all available pages before exploring further.

# F#

> F# is a functional-first programming language that runs on .NET. It combines functional programming with object-oriented and imperative programming paradigms, offering strong type inference, data immutability, and pattern matching.

<AccordionGroup>
  <Accordion title="F# Anti-Patterns Overview" icon="f-sharp">
    F#, despite being a well-designed functional language, has several common anti-patterns that can lead to code that's difficult to maintain, inefficient, or doesn't follow the language's idioms. Here are the most important anti-patterns to avoid when writing F# code.
  </Accordion>

  <Accordion title="Excessive Mutation" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Excessive mutation
    let processItems items =
        let mutable sum = 0
        let mutable count = 0
        
        for item in items do
            sum <- sum + item
            count <- count + 1
            
        if count > 0 then sum / count else 0

    // Better approach: Use functional constructs
    let processItems items =
        let items = Seq.toList items
        match items with
        | [] -> 0
        | _ -> items |> List.sum |> fun sum -> sum / List.length items
        
    // Even better
    let processItems items =
        items 
        |> Seq.fold (fun (sum, count) item -> (sum + item, count + 1)) (0, 0)
        |> function 
           | (_, 0) -> 0
           | (sum, count) -> sum / count
    ```

    Avoid excessive use of mutable variables and imperative loops. F# is a functional-first language, so prefer immutable data and functional constructs like pattern matching, recursion, and higher-order functions.
  </Accordion>

  <Accordion title="Not Leveraging the Type System" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Not leveraging the type system
    type User = {
        Name: string
        Email: string
        Status: string  // Could be "active", "inactive", "pending"
    }

    let isUserActive user =
        user.Status = "active"

    // Better approach: Use discriminated unions
    type UserStatus =
        | Active
        | Inactive
        | Pending

    type User = {
        Name: string
        Email: string
        Status: UserStatus
    }

    let isUserActive user =
        match user.Status with
        | Active -> true
        | _ -> false
    ```

    Leverage F#'s powerful type system, especially discriminated unions, to model your domain accurately. This provides compile-time safety and makes your code more expressive and self-documenting.
  </Accordion>

  <Accordion title="Ignoring Option Types" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Ignoring option types
    let findUser (users: User list) (id: string) =
        users |> List.find (fun u -> u.Id = id)  // Throws if not found

    // Usage that might throw
    let user = findUser users "123"
    printfn "Found user: %s" user.Name

    // Better approach: Use option types
    let findUser (users: User list) (id: string) =
        users |> List.tryFind (fun u -> u.Id = id)

    // Safe usage with pattern matching
    match findUser users "123" with
    | Some user -> printfn "Found user: %s" user.Name
    | None -> printfn "User not found"

    // Or using Option.map and defaultValue
    findUser users "123"
    |> Option.map (fun user -> user.Name)
    |> Option.defaultValue "Unknown user"
    |> printfn "User: %s"
    ```

    Use option types (`'T option`) to represent values that might not exist, rather than throwing exceptions or using null. This makes the possibility of missing values explicit in your type signatures and forces clients to handle both cases.
  </Accordion>

  <Accordion title="Excessive Object-Oriented Style" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Excessive OO style
    type Customer() =
        let mutable name = ""
        let mutable email = ""
        
        member this.Name
            with get() = name
            and set(value) = name <- value
            
        member this.Email
            with get() = email
            and set(value) = email <- value
            
        member this.IsValid() =
            not (String.IsNullOrEmpty(name)) && email.Contains("@")

    // Usage
    let customer = Customer()
    customer.Name <- "John"
    customer.Email <- "john@example.com"
    if customer.IsValid() then
        // Do something

    // Better approach: Use immutable records
    type Customer = {
        Name: string
        Email: string
    }

    let isValid customer =
        not (String.IsNullOrEmpty(customer.Name)) && customer.Email.Contains("@")

    // Usage
    let customer = { Name = "John"; Email = "john@example.com" }
    if isValid customer then
        // Do something
    ```

    Avoid excessive object-oriented style with mutable classes and properties. Instead, prefer immutable records and standalone functions. This leads to code that's easier to reason about and test.
  </Accordion>

  <Accordion title="Not Using Pattern Matching" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Not using pattern matching
    let describe x =
        if x = 0 then
            "Zero"
        else if x > 0 then
            "Positive"
        else
            "Negative"

    let processShape shape =
        if shape.GetType() = typeof<Circle> then
            let circle = shape :?> Circle
            Math.PI * circle.Radius * circle.Radius
        else if shape.GetType() = typeof<Rectangle> then
            let rect = shape :?> Rectangle
            rect.Width * rect.Height
        else
            0.0

    // Better approach: Use pattern matching
    let describe x =
        match x with
        | 0 -> "Zero"
        | x when x > 0 -> "Positive"
        | _ -> "Negative"

    // With discriminated unions
    type Shape =
        | Circle of radius: float
        | Rectangle of width: float * height: float
        | Triangle of base': float * height: float

    let area shape =
        match shape with
        | Circle radius -> Math.PI * radius * radius
        | Rectangle(width, height) -> width * height
        | Triangle(base', height) -> base' * height / 2.0
    ```

    Use pattern matching extensively, especially with discriminated unions. Pattern matching makes your code more concise, readable, and helps ensure you handle all possible cases.
  </Accordion>

  <Accordion title="Excessive Type Annotations" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Excessive type annotations
    let add (x: int) (y: int) : int =
        x + y

    let process (items: int list) : float =
        let sum: int = List.sum items
        let count: int = List.length items
        float sum / float count

    // Better approach: Let type inference work
    let add x y = x + y

    let process items =
        let sum = List.sum items
        let count = List.length items
        float sum / float count
    ```

    Avoid excessive type annotations. F# has powerful type inference, so you usually don't need to specify types explicitly. Add type annotations only when necessary for clarity, to resolve ambiguities, or at API boundaries.
  </Accordion>

  <Accordion title="Not Using Pipe Operator" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Not using the pipe operator
    let result = List.map (fun x -> x * 2) (List.filter (fun x -> x % 2 = 0) [1..10])

    // Better approach: Use the pipe operator
    let result =
        [1..10]
        |> List.filter (fun x -> x % 2 = 0)
        |> List.map (fun x -> x * 2)
    ```

    Use the pipe operator (`|>`) to create readable data processing pipelines. The pipe operator makes the flow of data through transformations clear and reduces the need for nested function calls or intermediate variables.
  </Accordion>

  <Accordion title="Ignoring Partial Application" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Ignoring partial application
    let filter predicate list =
        List.filter predicate list

    let map mapper list =
        List.map mapper list

    // Better approach: Leverage partial application
    let filter predicate =
        List.filter predicate

    let map mapper =
        List.map mapper

    // Usage
    let evenNumbers =
        [1..10]
        |> filter (fun x -> x % 2 = 0)
        |> map (fun x -> x * 2)
    ```

    Leverage partial application to create specialized functions from more general ones. This allows for more concise and composable code.
  </Accordion>

  <Accordion title="Not Using Active Patterns" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Complex pattern matching without active patterns
    let categorizeNumber x =
        match x with
        | x when x = 0 -> "Zero"
        | x when x > 0 && x % 2 = 0 -> "Positive even"
        | x when x > 0 -> "Positive odd"
        | x when x % 2 = 0 -> "Negative even"
        | _ -> "Negative odd"

    // Better approach: Use active patterns
    let (|Zero|Positive|Negative|) x =
        if x = 0 then Zero
        elif x > 0 then Positive x
        else Negative x

    let (|Even|Odd|) x =
        if x % 2 = 0 then Even x else Odd x

    let categorizeNumber x =
        match x with
        | Zero -> "Zero"
        | Positive(Even _) -> "Positive even"
        | Positive(Odd _) -> "Positive odd"
        | Negative(Even _) -> "Negative even"
        | Negative(Odd _) -> "Negative odd"
    ```

    Use active patterns to encapsulate complex matching logic and make pattern matching more readable and maintainable. Active patterns allow you to abstract away the details of how values are matched.
  </Accordion>

  <Accordion title="Not Using Units of Measure" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Not using units of measure
    let distance = 5.0  // Is this meters, feet, or miles?
    let time = 2.0      // Is this hours, minutes, or seconds?
    let speed = distance / time

    // Better approach: Use units of measure
    [<Measure>] type m
    [<Measure>] type s
    [<Measure>] type h

    let distance = 5.0<m>
    let time = 2.0<s>
    let speed = distance / time  // Type is float<m/s>

    // Convert to km/h
    [<Measure>] type km
    let kmPerHour = (speed * 3600.0<s/h>) * (1.0<km/1000.0<m>>)
    ```

    Use units of measure to add physical units to your numeric types. This prevents mixing incompatible quantities and makes your code more self-documenting and less prone to errors.
  </Accordion>

  <Accordion title="Not Using Computation Expressions" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Nested monadic operations without computation expressions
    let getUserData userId =
        match tryFindUser userId with
        | None -> Error "User not found"
        | Some user ->
            match tryGetUserPreferences user.Id with
            | None -> Error "Preferences not found"
            | Some prefs ->
                match tryGetRecommendations prefs with
                | None -> Error "No recommendations"
                | Some recs -> Ok (user, prefs, recs)

    // Better approach: Use computation expressions
    let getUserData userId = result {
        let! user = tryFindUser userId
        let! prefs = tryGetUserPreferences user.Id
        let! recs = tryGetRecommendations prefs
        return (user, prefs, recs)
    }

    // Another example with async
    let fetchData url = async {
        let! response = httpClient.GetAsync(url) |> Async.AwaitTask
        let! content = response.Content.ReadAsStringAsync() |> Async.AwaitTask
        return content
    }
    ```

    Use computation expressions (like `async`, `task`, `result`, `option`, etc.) to simplify working with monadic types. They make your code more readable by eliminating nested pattern matching and allowing a more imperative-like syntax for functional concepts.
  </Accordion>

  <Accordion title="Not Using Type Providers" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Manual data access code
    let getCustomers() =
        use connection = new SqlConnection(connectionString)
        connection.Open()
        use command = new SqlCommand("SELECT Id, Name, Email FROM Customers", connection)
        use reader = command.ExecuteReader()
        
        let customers = ResizeArray<Customer>()
        while reader.Read() do
            let customer = {
                Id = reader.GetInt32(0)
                Name = reader.GetString(1)
                Email = reader.GetString(2)
            }
            customers.Add(customer)
            
        customers |> Seq.toList

    // Better approach: Use type providers
    type Sql = SqlDataProvider<...>  // Configuration omitted for brevity

    let getCustomers() =
        use context = Sql.GetDataContext()
        query {
            for customer in context.Customers do
            select { 
                Id = customer.Id
                Name = customer.Name
                Email = customer.Email
            }
        }
        |> Seq.toList
    ```

    Use type providers to access external data sources like databases, web services, or file formats. Type providers generate types based on the schema of the data source, providing compile-time checking and IntelliSense support.
  </Accordion>

  <Accordion title="Excessive Recursion Without Tail Calls" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Recursion without tail calls
    let rec factorial n =
        if n <= 1 then 1
        else n * factorial (n - 1)  // Not tail-recursive

    // Better approach: Use tail recursion
    let factorial n =
        let rec loop n acc =
            if n <= 1 then acc
            else loop (n - 1) (n * acc)  // Tail-recursive
        loop n 1
    ```

    When using recursion, make your functions tail-recursive to avoid stack overflow errors with large inputs. In a tail-recursive function, the recursive call is the last operation in the function.
  </Accordion>

  <Accordion title="Not Using Modules and Namespaces Effectively" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Everything in one file without organization
    // MyApp.fs
    module MyApp

    // Types, functions, and values all mixed together

    // Better approach: Organize code into modules and namespaces
    // Domain.fs
    namespace MyApp.Domain

    type User = { /* ... */ }
    type Order = { /* ... */ }

    // Services.fs
    namespace MyApp.Services

    open MyApp.Domain

    module UserService =
        let findUser userId = // ...
        let updateUser user = // ...

    module OrderService =
        let placeOrder userId items = // ...
        let cancelOrder orderId = // ...
    ```

    Organize your code into modules and namespaces based on functionality. This improves maintainability, compilation times, and allows for better encapsulation of implementation details.
  </Accordion>

  <Accordion title="Not Using Railway-Oriented Programming" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Exception-based error handling
    let validateEmail email =
        if String.IsNullOrEmpty(email) then
            raise (ArgumentException("Email cannot be empty"))
        elif not (email.Contains("@")) then
            raise (ArgumentException("Email must contain @"))
        else
            email

    let validatePassword password =
        if String.IsNullOrEmpty(password) then
            raise (ArgumentException("Password cannot be empty"))
        elif password.Length < 8 then
            raise (ArgumentException("Password must be at least 8 characters"))
        else
            password

    // Better approach: Railway-oriented programming
    type ValidationResult<'T> = Result<'T, string>

    let validateEmail email : ValidationResult<string> =
        if String.IsNullOrEmpty(email) then
            Error "Email cannot be empty"
        elif not (email.Contains("@")) then
            Error "Email must contain @"
        else
            Ok email

    let validatePassword password : ValidationResult<string> =
        if String.IsNullOrEmpty(password) then
            Error "Password cannot be empty"
        elif password.Length < 8 then
            Error "Password must be at least 8 characters"
        else
            Ok password

    // Compose validations
    let validateCredentials email password =
        result {
            let! validEmail = validateEmail email
            let! validPassword = validatePassword password
            return (validEmail, validPassword)
        }
    ```

    Use railway-oriented programming (using `Result<'T, 'Error>` or similar types) for error handling instead of exceptions. This makes error paths explicit in your code and allows for better composition of functions that might fail.
  </Accordion>

  <Accordion title="Not Using Async and Task Properly" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Blocking async code
    let fetchData url =
        async {
            let! response = httpClient.GetAsync(url) |> Async.AwaitTask
            return response
        } |> Async.RunSynchronously  // Blocks the thread

    // Better approach: Keep async workflows async
    let fetchData url =
        async {
            let! response = httpClient.GetAsync(url) |> Async.AwaitTask
            return response
        }

    // Usage
    let processDataAsync() = async {
        let! data = fetchData "https://example.com/api/data"
        let! moreData = fetchData "https://example.com/api/more-data"
        return processResults data moreData
    }

    // Only at the top level or entry point
    let main args =
        processDataAsync() |> Async.RunSynchronously
    ```

    Use `Async` and `Task` properly for asynchronous operations. Don't block async workflows with `Async.RunSynchronously` except at the top level of your application. Compose async operations using computation expressions.
  </Accordion>

  <Accordion title="Not Using Immutable Collections" icon="warning">
    ```fsharp theme={null}
    // Anti-pattern: Using mutable collections
    let processItems items =
        let results = ResizeArray<int>()
        for item in items do
            if item % 2 = 0 then
                results.Add(item * 2)
        results |> Seq.toList

    // Better approach: Use immutable collections and transformations
    let processItems items =
        items
        |> List.filter (fun item -> item % 2 = 0)
        |> List.map (fun item -> item * 2)
    ```

    Prefer immutable collections (`list`, `seq`, `array`, etc.) and functional transformations over mutable collections and in-place modifications. This leads to code that's easier to reason about and less prone to bugs.
  </Accordion>
</AccordionGroup>
