Skip to content
Back to skills

Fsharp

ASecurity

Write, review, or modernize F# code in .NET repositories with functional-first design, algebraic data types, pattern matching, pipelines, async workflows, project ordering, and C# interop. USE FOR: F# .fs/.fsproj code; discriminated unions, records, options, results, pattern matching, computation expressions, or functional domain modeling; strongly typed AI-generated .NET code. DO NOT USE FOR: C# language modernization; FSI-only exploratory scripts with no project changes. INVOKES: inspect .f...

  • 485 stars
  • 0 votes
  • 2 copies
  • 3 views
  • Added May 27, 2026
developmentc#bashexpressapi

Works with

  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add managedcode/dotnet-skills --skill fsharp --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Fsharp?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Fsharp
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/managedcode-fsharp/badge)](https://www.skillsdirectory.com/skills/managedcode-fsharp)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: fsharp
description: "Write, review, or modernize F# code in .NET repositories with functional-first design, algebraic data types, pattern matching, pipelines, async workflows, project ordering, and C# interop. USE FOR: F# .fs/.fsproj code; discriminated unions, records, options, results, pattern matching, computation expressions, or functional domain modeling; strongly typed AI-generated .NET code. DO NOT USE FOR: C# language modernization; FSI-only exploratory scripts with no project changes. INVOKES: inspect .fsproj file order and SDK settings, edit F# source and project files, and run dotnet build/test or dotnet fsi validation commands when changes are made."
compatibility: "Requires a .NET SDK with F# support and an F# project, script, or interop boundary."
---

# F# for .NET

## Trigger On

- the task touches `.fs`, `.fsx`, `.fsi`, or `.fsproj` files
- domain logic benefits from records, discriminated unions, options, results, or exhaustive pattern matching
- generated code needs a strongly typed functional model instead of nullable primitive state
- F# code must interoperate with C# or other .NET libraries
- the repository needs project setup, source ordering, or validation for F# code

## Do Not Use For

- C# language feature selection; use `modern-csharp`
- analyzer-only, formatter-only, or CI-only work with no F# language decisions
- one-off REPL or scripting exploration that does not affect project code; use `fsi`
- non-.NET functional languages

## Project Setup

Use the .NET SDK templates when adding F# projects.

```bash
dotnet new classlib -lang "F#" -o src/Domain
dotnet new console -lang "F#" -o src/App
dotnet new xunit -lang "F#" -o tests/Domain.Tests
dotnet sln add src/Domain/Domain.fsproj tests/Domain.Tests/Domain.Tests.fsproj
dotnet add tests/Domain.Tests/Domain.Tests.fsproj reference src/Domain/Domain.fsproj
dotnet build
```

`FSharp.Core` is normally supplied by the F# SDK project. Add or pin it explicitly only when the repo has a package policy that requires deterministic package versions.

```bash
dotnet add src/Domain/Domain.fsproj package FSharp.Core
```

## Source Ordering

F# compiles files in the order listed in the project file. Define types and modules before files that consume them.

```xml
<ItemGroup>
  <Compile Include="Domain.fs" />
  <Compile Include="Validation.fs" />
  <Compile Include="Program.fs" />
</ItemGroup>
```

When adding a file, update the `.fsproj` intentionally. Do not assume wildcard ordering.

## Workflow

1. Inspect the existing `.fsproj`, `.fs`, `.fsi`, and `.fsx` files to determine compile order, public boundaries, and whether the code is F#-first or C#-facing.
2. Choose the smallest F# model that represents the invariant: records for named product data, discriminated unions for closed state, `option` for absence, and `Result` for expected failures.
3. Add or edit source files in dependency order, then update the `.fsproj` compile list before consumers reference the new code.
4. Review public API shape for .NET interop. Translate F#-specific internal models to DTOs, methods, or `Try*` patterns when C# callers need a stable surface.
5. Run `dotnet build`, targeted tests, and any relevant `dotnet fsi` probes before returning the final result.

## Practical Patterns

### Model State With Records And Unions

Prefer a type that names every valid state over loose strings, nullable values, or parallel booleans.

```fsharp
module Orders

type OrderId = private OrderId of string

module OrderId =
    let tryCreate value =
        if System.String.IsNullOrWhiteSpace value then
            Error "Order id is required."
        else
            Ok (OrderId value)

type Payment =
    | Card of last4: string
    | Wire of iban: string
    | PurchaseOrder of number: string

type Order =
    { Id: OrderId
      Customer: string
      Payment: Payment }

let describePayment payment =
    match payment with
    | Card last4 -> $"card ending {last4}"
    | Wire iban -> $"wire transfer {iban}"
    | PurchaseOrder number -> $"purchase order {number}"
```

### Compose Validation With Result

Return `Result<'T,'Error>` when callers must handle failure and when failures are part of the domain.

```fsharp
module Pricing

type Price =
    private
    | Price of decimal

module Price =
    let tryCreate amount =
        if amount < 0m then
            Error "Price cannot be negative."
        else
            Ok (Price amount)

    let value (Price amount) = amount

type Line =
    { Sku: string
      Quantity: int
      UnitPrice: Price }

let tryCreateLine sku quantity amount =
    match Price.tryCreate amount with
    | Ok price when quantity > 0 ->
        Ok { Sku = sku; Quantity = quantity; UnitPrice = price }
    | Ok _ ->
        Error "Quantity must be positive."
    | Error message ->
        Error message
```

### Read And Transform Data

Use pipelines for readable transformations, but stop before the pipeline hides error handling or allocation cost.

```fsharp
open System.IO

let readActiveUsers path =
    File.ReadLines path
    |> Seq.skip 1
    |> Seq.choose (fun line ->
        match line.Split(',') with
        | [| id; name; "active" |] -> Some {| Id = id; Name = name |}
        | _ -> None)
    |> Seq.toList
```

### Write A Small .NET Boundary

Make public interop APIs easy for C# callers. Hide F#-specific details behind functions, methods, or DTO records when needed.

```fsharp
namespace Company.Domain

type InvoiceDto =
    { Id: string
      Total: decimal
      IsPaid: bool }

module Invoice =
    let markPaid invoice =
        { invoice with IsPaid = true }
```

## Interop Guidance

- Keep F# discriminated unions inside F# boundaries unless C# consumers are expected to understand their generated shape.
- Use records or explicit classes for public cross-language DTOs.
- Use `option` internally; translate to nullable annotations, `Try*` methods, or `Result` at C#-first public boundaries.
- Avoid throwing for expected domain failures. Use exceptions for unexpected infrastructure failures.
- Prefer `task { }` or `Task`-returning APIs at .NET interop boundaries; use `Async<'T>` when the code is F#-first.

## Review Checklist

- The `.fsproj` compile order matches dependency order.
- All union cases are handled explicitly; wildcard branches are justified.
- Public types have stable names and shapes for downstream .NET consumers.
- Domain failures are modeled with `option` or `Result`, not undocumented `null`.
- Pipelines remain readable and do not hide repeated enumeration of expensive sequences.
- Tests cover at least one success and one failure path for each domain constructor or validator.

## Validate

Run the narrowest relevant checks after changes:

```bash
dotnet build
dotnet test
dotnet fsi scripts/check.fsx
```

If adding a new F# file, also inspect the project file diff to confirm the compile order is deliberate.

## Sources

- https://learn.microsoft.com/dotnet/fsharp/what-is-fsharp
- https://learn.microsoft.com/dotnet/fsharp/get-started/get-started-command-line
- https://learn.microsoft.com/dotnet/fsharp/language-reference/discriminated-unions
- https://learn.microsoft.com/dotnet/fsharp/language-reference/pattern-matching
- https://learn.microsoft.com/dotnet/fsharp/language-reference/results

Files in this skill

  • SKILL.md7.1 KB
  • manifest.json86 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.