跳转至

F# 编码风格

本文件使用 F# 特定内容扩展 common/coding-style.md。

标准

  • 遵循标准 F# 约定,利用类型系统确保准确性
  • 默认优先使用不可变性。仅在性能原因下使用 mutable
  • 保持模块聚焦和高内聚

类型与模型

  • 领域建模优先使用判别联合而非类层次结构
  • 对具有命名字段的数据使用记录
  • 对基本类型使用单案例联合作为类型安全包装
  • 除非需要互操作或可变状态,否则避免使用类
type EmailAddress = EmailAddress of string

type OrderStatus =
    | Pending
    | Confirmed of confirmedAt: DateTimeOffset
    | Shipped of trackingNumber: string
    | Cancelled of reason: string

type Order =
    { Id: Guid
      CustomerId: string
      Status: OrderStatus
      Items: OrderItem list }

不可变性

  • 记录默认不可变。使用 with 表达式进行更新
  • 优先使用 list、map、set 而非可变集合
  • 在领域逻辑中避免使用 ref 单元格和可变字段
let rename (profile: UserProfile) newName =
    { profile with Name = newName }

函数式风格

  • 优先使用小而可组合的函数而非大型方法
  • 使用管道运算符 |> 构建可读的数据管道
  • 优先使用模式匹配而非 if/else 链
  • 使用 Option 代替 null。对可能失败的操作使用 Result
let processOrder order =
    order
    |> validateItems
    |> Result.bind calculateTotal
    |> Result.map applyDiscount
    |> Result.mapError OrderError

异步与错误处理

  • 与 .NET 异步 API 互操作时使用 task { }
  • 对 F# 原生异步工作流使用 async { }
  • 通过公共异步 API 传播 CancellationToken
  • 对预期错误优先使用 Result 和铁路导向编程而非异常
let loadOrderAsync (orderId: Guid) (ct: CancellationToken) =
    task {
        let! order = repository.FindAsync(orderId, ct)
        return
            order
            |> Option.defaultWith (fun () ->
                failwith $"Order {orderId} was not found.")
    }

格式

  • 使用 fantomas 进行自动格式化
  • 优先使用有意义的空白。避免不必要的括号
  • 删除未使用的 open 声明

open 声明顺序

open 语句用空行分成四个部分进行分组,每个部分内按字典顺序排列:

  1. System.*
  2. Microsoft.*
  3. 第三方命名空间
  4. 第一方 / 项目命名空间
open System
open System.Collections.Generic
open System.Threading.Tasks

open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Logging

open FsCheck.Xunit
open Swensen.Unquote

open MyApp.Domain
open MyApp.Infrastructure

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