Nw Fp FsharpSAFE
AI agents that guide you from idea to working code, with you in control at every step.
Overview
AI agents that guide you from idea to working code, with you in control at every step.
1a864ec81cdeOBSERVED · 2026-10-14What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
---
name: nw-fp-fsharp
agent: nw-functional-software-crafter
description: F# language-specific patterns, Railway-Oriented Programming, and Computation Expressions
user-invocable: false
disable-model-invocation: true
---
# FP in F# -- Functional Software Crafter Skill
Cross-references: [fp-principles](../nw-fp-principles/SKILL.md) | [fp-domain-modeling](../nw-fp-domain-modeling/SKILL.md) | [property-based-testing](../nw-property-based-testing/SKILL.md)
## When to Choose F#
- Best for: domain modeling on .NET | DDD | railway-oriented programming | pipeline-first design | finance
- Not ideal for: teams needing higher-kinded types | non-.NET platforms | large existing C# codebases resistant to change
## [STARTER] Quick Setup
```bash
dotnet new console -lang F# -o OrderService && cd OrderService
dotnet new xunit -lang F# -o OrderService.Tests
dotnet add OrderService.Tests reference OrderService
dotnet add OrderService.Tests package FsCheck.Xunit
dotnet test
```
**File order matters**: F# compiles files top-to-bottom as listed in `.fsproj`. Types must be defined before use.
## [STARTER] Type System for Domain Modeling
### Choice Types (Discriminated Unions)
```fsharp
type PaymentMethod =
| CreditCard of cardNumber: string * expiryDate: string
| BankTransfer of accountNumber: string
| Cash
```
### Record Types and Domain Wrappers
```fsharp
type Customer = {
CustomerId: CustomerId
CustomerName: CustomerName
CustomerEmail: EmailAddress
}
type OrderId = OrderId of int
type EmailAddress = EmailAddress of string
```
Records have structural equality by default. Single-case DUs have small runtime cost (unlike Haskell's zero-cost newtype).
### [STARTER] Validated Construction (Smart Constructors)
```fsharp
module EmailAddress =
let create (rawEmail: string) : Result<EmailAddress, string> =
if rawEmail.Contains("@") then Ok (EmailAddress rawEmail)
else Error $"Invalid email: {rawEmail}"
let value (EmailAddress email) = email
```
## [INTERMEDIATE] Composition Style
### Pipeline Operator (The Defining Feature)
```fsharp
let processOrder rawOrder =
rawOrder
|> validateOrder
|> Result.bind priceOrder
|> Result.bind confirmOrder
|> Result.map generateReceipt
```
**Data-last convention**: F# functions put primary input last so they compose with `|>`.
### Railway-Oriented Programming (Error-Track Pipelines)
```fsharp
let placeOrder unvalidatedOrder =
unvalidatedOrder
|> validateOrder
|> Result.bind priceOrder
|> Result.bind confirmOrder
|> Result.mapError PlaceOrderError.Validation
```
### Computation Expressions for Monadic Syntax
```fsharp
open FsToolkit.ErrorHandling
let placeOrder rawOrder = result {
let! validated = validateOrder rawOrder
let! priced = priceOrder validated
return! confirmOrder priced
}
```
Key builders: `result { }` (error-track) | `async { }` (async I/O) | `task { }` (.NET Task interop) | `validation { }` (accumulate errors, FsToolkit).
## [INTERMEDIATE] Effect Management
F# is impure by default. Purity maintained by architectural convention, not the compiler.
### Pure Core / Imperative Shell
```fsharp
// Pure domain logic (no I/O, no mutation)
module Domain =
let calculateDiscount (order: Order) : Discount =
if List.length order.OrderLines > 10 then Discount 0.1m
else Discount 0.0m
// Imperative shell (I/O at edges)
module App =
let placeOrderHandler (deps: Dependencies) (rawOrder: UnvalidatedOrder) = async {
let! result =
rawOrder
|> Domain.validateOrder deps.CheckProductExists
|> Result.bind (Domain.priceOrder deps.GetProductPrice)
do! deps.SaveOrder result
return result
}
```
### [ADVANCED] Hexagonal Architecture via Partial Application
```fsharp
// Ports as function types
type FindOrder = OrderId -> Async<Order option>
type SaveOrder = Order -> Async<unit>
// Adapter: concrete implementation
let findOrderInDb (connStr: string) (orderId: OrderId) : Async<Order option> =
async { (* database query *) }
// Composition root: partially apply dependencies
let findOrder = findOrderInDb "Server=localhost;Database=orders"
```
Dependencies first, primary input last. Partially apply at composition root.
## [INTERMEDIATE] Testing
**Frameworks**: FsCheck (QuickCheck port) | fsharp-hedgehog (integrated shrinking) | Expecto (F#-native) | Unquote (assertions). See [property-based-testing](../nw-property-based-testing/SKILL.md) for detailed PBT patterns.
### Property Test Example (FsCheck + xUnit)
```fsharp
open FsCheck.Xunit
[<Property>]
let ``validated orders always have positive totals`` (rawOrder: RawOrder) =
match validateOrder rawOrder with
| Error _ -> true
| Ok valid -> orderTotal valid > Money 0m
[<Property>]
let ``serialization round-trips`` (order: Order) =
order |> serialize |> deserialize = Ok order
```
### Custom Generator
```fsharp
let genValidEmail = gen {
let! user = Gen.nonEmptyListOf (Gen.elements ['a'..'z']) |> Gen.map (fun cs -> System.String(Array.ofList cs))
let! domain = Gen.nonEmptyListOf (Gen.elements ['a'..'z']) |> Gen.map (fun cs -> System.String(Array.ofList cs))
return EmailAddress $"{user}@{domain}.com"
}
```
## [ADVANCED] Idiomatic Patterns
### Document Lifecycle as Separate Types
```fsharp
type UnvalidatedOrder = { RawName: string; RawEmail: string; RawLines: string list }
type ValidatedOrder = { Name: CustomerName; Email: EmailAddress; Lines: OrderLine list }
type PricedOrder = { ValidOrder: ValidatedOrder; Total: Money; Lines: PricedOrderLine list }
```
Each stage is a distinct type. Pipeline transforms one into the next.
### Collect-All-Errors Validation
```fsharp
open FsToolkit.ErrorHandling
let validateCustomer (raw: RawCustomer) = validation {
let! name = validateName raw.Name
and! email = validateEmail raw.Email
and! address = validateAddress raw.Address
return { Name = name; EmTrust audit
SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | NA |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- none-observed
- Shell
- none-observed
- Dependencies
- pinned
- Secrets in source
- none-found
Findings (0)
No findings outside the package's declared scope.
Gates applied: no_behavioural_pass.
1a864ec81cdefull audit observations/trust-audit/skill/nwave-ai__nw-fp-fsharp.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-14 | 1a864ec81cde | SAFE | B | 89 | first audit |
Questions
What does the Nw Fp Fsharp skill do?
AI agents that guide you from idea to working code, with you in control at every step.
Is Nw Fp Fsharp safe to install?
The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean skill reads B.
What can Nw Fp Fsharp access on my machine?
The audit observed no filesystem, network or shell use at all in its source.
How current is this page?
The grade is for one exact copy of the source (1a864ec81cde), read on 2026-10-14. The repository is watched, and a new audit runs when it changes — this is the first audit.