Validated是一个小型的μ库(~50行代码),通过提供用于轻松生成具有内置保证的新类型的工具,允许您更好地利用Swift的类型系统。
Validated允许您使用类型系统来验证值的属性,提供编译时保证的新层次。
使用验证器,您可以定义新的类型,这些类型向现有类型添加保证
// Create a new string type that can never be empty
typealias NonEmptyString = Validated<String, NonEmptyStringValidator>
您可能有一个函数只擅长处理当用户已登录时的User
值。通常,您会在代码中实现此要求并添加文档,但您没有简单的方法来在类型签名中表达此不变性
/// Please ever only call with a logged-in user!
func performTaskWithUser(user: User) {
precondition(
user.loggedIn,
"It is illegal to call this method with a logged out user!"
)
// ...
}
使用Validated,您可以快速创建一个新的类型来描述类型系统中的此要求。这使得无法用未登录用户调用该函数,并使得方法签名表达出您的不变性(而不是依赖于文档)
func performTaskWithUser(user: LoggedInUser) {
// ...
}
那么这个新的LoggedInUser
类型是如何创建的呢?
首先,您需要实现一个验证器
struct LoggedInValidator: Validator {
static func validate(value: User) -> Bool {
return value.loggedIn
}
}
一个Validator
需要实现一个validate
函数,该函数接受此验证器可以验证的类型(在这种情况下是User
)。该函数返回一个Bool
。如果满足要求则返回true
,否则返回false
。
有了Validator
,我们可以这样创建我们的新类型:
typealias LoggedInUser = Validated<User, LoggedInValidator>
请注意,不需要提供typealias,但对于大多数情况,这是推荐的。
就这样!
LoggedInUser
现在有一个失败的初始化器,它接受一个User
。如果传入的User
满足已登录的要求,则您将拥有一个LoggedInUser
,否则为nil
。此外,LoggedInUser
还提供了一个抛出初始器的功能,以便您可以选择将失败验证当作错误来处理,而不是nil
值。
底层值(完整的User
值)存储在LoggedInUser
的.value
属性中。
Validated提供了一些可能不是那么显而易见的额外功能。
Validated 提供了 Validator
类型,用于逻辑操作,允许您以不同的组合要求多个验证。例如,您可以使用 And
验证器要求必须满足两个条件才能初始化您的类型。
typealias AllCapsNonEmptyString =
Validated<String, And<NonEmptyStringValidator, AllCapsLatinStringValidator>>
Or
和 Not
也作为辅助验证器提供。您可以查看规范以获取更多示例。
Validator
本身可以是泛型的。如果您想对整个类型类别提供验证,这非常有用。示例验证器 NonEmptyCollectionValidator
通过使用泛型要求可以应用于所有验证器类型。
struct NonEmptyCollectionValidator<T: CollectionType>: Validator {
static func validate(value: T) -> Bool {
if !value.isEmpty {
return true
} else {
return false
}
}
}
但是,当使用此验证器创建类型时,您必须指定要验证的确切集合类型。
typealias NonEmptyListOfStrings = Validated<[String], NonEmptyCollectionValidator<[String]>>
不是真正意义上的。依赖类型使我们能够仅基于值来定义类型。该库仅允许我们根据值和验证器验证一个类型是否为指定类型 <T>
。值本身并不会改变类型信息。
真正的依赖类型会产生以下结果:
[1, 2, 3] // type = Array<count:3 type:Int>
[1, 2, 3, 4] // type = Array<count:4 type:Int>
使用 Validated,我们只能验证一个类型是否属于我们静态定义的某个类别。
ListOf3([1,2,3]) // type = ListOf3<Array<Int>
ListOf3([1,2,3,4]) // type = nil
然而,这些静态提供的检查仍然可以为您的代码增加很多价值;请参见上面的示例。
Validated 通过常规方式提供。
如果您有任何问题,您可以在twitter上找到我 @benjaminencz。