跳转至

F# 模式

此文件以 F# 特有的内容扩展了 common/patterns.md。

用于错误处理的 Result 类型

对于预期错误,请遵循 Railway 导向编程并使用 Result<'T, 'TError>,而不是异常。

type OrderError =
    | InvalidCustomer of string
    | EmptyItems
    | ItemOutOfStock of sku: string

let validateOrder (request: CreateOrderRequest) : Result<ValidatedOrder, OrderError> =
    if String.IsNullOrWhiteSpace request.CustomerId then
        Error(InvalidCustomer "CustomerId is required")
    elif request.Items |> List.isEmpty then
        Error EmptyItems
    else
        Ok { CustomerId = request.CustomerId; Items = request.Items }

用于缺失值的 Option

优先使用 Option<'T> 而不是 null。使用 Option.map、Option.bind、Option.defaultValue 进行转换。

let findUser (id: Guid) : User option =
    users |> Map.tryFind id

let getUserEmail userId =
    findUser userId
    |> Option.map (fun u -> u.Email)
    |> Option.defaultValue "unknown@example.com"

用于领域建模的可区分联合

显式地为业务状态建模。编译器会强制穷尽处理所有情况。

type PaymentState =
    | AwaitingPayment of amount: decimal
    | Paid of paidAt: DateTimeOffset * transactionId: string
    | Refunded of refundedAt: DateTimeOffset * reason: string
    | Failed of error: string

let describePayment = function
    | AwaitingPayment amount -> $"Awaiting payment of {amount:C}"
    | Paid (at, txn) -> $"Paid at {at} (txn: {txn})"
    | Refunded (at, reason) -> $"Refunded at {at}: {reason}"
    | Failed error -> $"Payment failed: {error}"

计算表达式

使用计算表达式简化可能失败的顺序操作。

let placeOrder request =
    result {
        let! validated = validateOrder request
        let! inventory = checkInventory validated.Items
        let! order = createOrder validated inventory
        return order
    }

模块组织

  • 将相关函数分组到模块而非类中
  • 使用 [<RequireQualifiedAccess>] 防止名称冲突
  • 模块应保持小巧,专注于单一职责
[<RequireQualifiedAccess>]
module Order =
    let create customerId items = { Id = Guid.NewGuid(); CustomerId = customerId; Items = items; Status = Pending }
    let confirm order = { order with Status = Confirmed(DateTimeOffset.UtcNow) }
    let cancel reason order = { order with Status = Cancelled reason }

依赖注入

  • 将依赖定义为函数参数或函数记录
  • 主要仅在 .NET 库的边界处使用接口
  • 优先使用部分应用向管道注入依赖
type OrderDeps =
    { FindOrder: Guid -> Task<Order option>
      SaveOrder: Order -> Task<unit>
      SendNotification: Order -> Task<unit> }

let processOrder (deps: OrderDeps) orderId =
    task {
        match! deps.FindOrder orderId with
        | None -> return Error "Order not found"
        | Some order ->
            let confirmed = Order.confirm order
            do! deps.SaveOrder confirmed
            do! deps.SendNotification confirmed
            return Ok confirmed
    }

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC