PointFree-Validated 0.2.1

PointFree-Validated 0.2.1

Stephen CelisBrandon Williams维护。



🛂validated

Swift 5.1 Build Status @pointfreeco

一个累加多个错误的结果类型。

目录

动机

问题

Swift的异常处理在第一次失败时中断。由于这一点,它并不适合处理如表单数据这样的情况,其中多个输入可能导致多个错误。

struct User {
  let id: Int
  let email: String
  let name: String
}

func validate(id: Int) throws -> Int {
  guard id > 0 else {
    throw Invalid.error("id must be greater than zero")
  }
  return id
}

func validate(email: String) throws -> String {
  guard email.contains("@") else {
    throw Invalid.error("email must be valid")
  }
  return email
}

func validate(name: String) throws -> String {
  guard !name.isEmpty else {
    throw Invalid.error("name can't be blank")
  }
  return name
}

func validateUser(id: Int, email: String, name: String) throws -> User {
  return User(
    id: try validate(id: id),
    email: try validate(id: email),
    name: try validate(id: name)
  )
}

我们将几个投掷函数组合成一个可以返回User的单一投掷函数。

let user = try validateUser(id: 1, email: "[email protected]", name: "Blob")
// User(id: 1, email: "[email protected]", name: "Blob")

如果idemailname无效,将会抛出错误。

let user = try validateUser(id: 1, email: "[email protected]", name: "")
// throws Invalid.error("name can't be blank")

不幸的是,如果这些输入中的几个或所有都是无效的,第一个错误将会胜出。

let user = try validateUser(id: -1, email: "blobpointfree.co", name: "")
// throws Invalid.error("id must be greater than zero")

使用Validated处理多个错误

Validated是一种类似于Result的类型,可以累积多个错误。而不是使用投掷函数,我们可以定义与Validated一起工作的函数。

func validate(id: Int) -> Validated<Int, String> {
  return id > 0
    ? .valid(id)
    : .error("id must be greater than zero")
}

func validate(email: String) -> Validated<String, String> {
  return email.contains("@")
    ? .valid(email)
    : .error("email must be valid")
}

func validate(name: String) -> Validated<String, String> {
  return !name.isEmpty
    ? .valid(name)
    : .error("name can't be blank")
}

为了累积错误,我们使用了一个可能我们已经很熟悉的功能:zip

let validInputs = zip(
  validate(id: 1),
  validate(email: "[email protected]"),
  validate(name: "Blob")
)
// Validated<(Int, String, String), String>

Validated上的zip函数在序列上的工作方式与在序列中使用的基本相同,但它不是将一对序列zip到一个由对组成的序列中,而是将一组单个的Validated值压缩成一个组内单独的Validated值。

从这里,我们可以使用另一个我们可能已经很熟悉的功能,map,它接受一个转换函数并产生一个新的其有效情况已被转换的Validated值。

let validUser = validInputs.map(User.init)
// valid(User(id: 1, email: "[email protected]", name: "Blob"))

我们有效的输入组已经转换成了一个有效的用户。

为了便捷性和组合性,提供了一个柯里化的zip(with:)函数,它接受一个转换函数和Validated输入。

zip(with: User.init)(
  validate(id: 1),
  validate(email: "[email protected]"),
  validate(name: "Blob")
)
// valid(User(id: 1, email: "[email protected]", name: "Blob"))

无效的输入会在invalid情况下产生一个错误。

zip(with: User.init)(
  validate(id: 1),
  validate(email: "[email protected]"),
  validate(name: "")
)
// invalid(["name can't be blank"])

更重要的是,多个无效输入会产生一个包含多个错误的invalid情况。

zip(with: User.init)(
  validate(id: -1),
  validate(email: "blobpointfree.co"),
  validate(name: "")
)
// invalid([
//   "id must be greater than zero",
//   "email must be valid",
//   "name can't be blank"
// ])

无效的错误被保存在一个非空数组中,以确保在编译时不会遇到空的invalid情况。

安装

Carthage

如果你使用Carthage,可以将以下依赖项添加到你的Cartfile

github "pointfreeco/swift-validated" ~> 0.2.1

CocoaPods

如果您的项目使用CocoaPods,只需将其添加到您的Podfile中。

pod 'PointFree-Validated', '~> 0.2.1'

SwiftPM

如果您想在使用SwiftPM的项目中使用Validated,只需在您的Package.swift中添加一个dependencies子句即可。

dependencies: [
  .package(url: "https://github.com/pointfreeco/swift-validated.git", from: "0.2.1")
]

Xcode 子项目

子模块、克隆或下载验证过的版本,并将 Validated.xcodeproj 拖动到你的项目中。

想了解更多信息?

这些概念(以及更多)在由 Brandon Williams 和 Stephen Celis 主办的 Point-Free 视频系列中进行了深入探讨,该视频系列探讨了函数式编程和 Swift。

Validated 在 The Many Faces of Zip: Part 2 中进行了探讨

video poster image

许可证

所有模块均在 MIT 许可证下发布。有关详细信息,请参阅 LICENSE