scala - 如何以正确的方式使用 Scala Cats Validated?
问题描述
以下是我的用例
- 我正在使用Cats来验证我的配置。我的配置文件在 json 中。
Config
我使用lift-json将我的配置文件反序列化为我的案例类,然后使用 Cats 对其进行验证。我以此为指导。- 我使用 Cats 的动机是收集验证时出现的所有错误。
我的问题是指南中给出的示例,属于类型
case class Person(name: String, age: Int)
def validatePerson(name: String, age: Int): ValidationResult[Person] = {
(validateName(name),validate(age)).mapN(Person)
}
但在我的情况下,我已经将我的配置反序列化到我的案例类中(下面是一个示例),然后我将它传递给验证
case class Config(source: List[String], dest: List[String], extra: List[String])
def vaildateConfig(config: Config): ValidationResult[Config] = {
(validateSource(config.source), validateDestination(config.dest))
.mapN { case _ => config }
}
这里的区别是mapN { case _ => config }
。因为如果一切都有效,我已经有了一个配置,我不想从它的成员重新创建配置。当我传递配置来验证函数而不是它的成员时,就会出现这种情况。
我工作场所的一个人告诉我这不是正确的方法,因为 Cats Validated 提供了一种在其成员有效的情况下构造对象的方法。如果其成员无效,则该对象不应存在或不可构造。这对我来说完全有意义。
那么我应该做任何改变吗?以上我做的可以接受吗?
PS:上面的配置只是一个例子,我的真实配置可以有其他案例类作为它的成员,它们本身可以依赖于其他案例类。
解决方案
像 Cats 这样的库所提倡的那种编程的中心目标之一是使无效状态无法表示。在一个完美的世界中,根据这种哲学,不可能创建一个Config
带有无效成员数据的实例(通过使用像Refined这样的库,其中复杂的约束可以在类型系统中表达和跟踪,或者简单地通过隐藏不安全的构造函数)。在一个稍微不那么完美的世界中,仍然可以构造 的无效实例Config
,但不鼓励,例如通过使用安全构造函数(如您的validatePerson
方法 for Person
)。
听起来您处于一个更不完美的世界中,您的实例Config
可能包含也可能不包含无效数据,并且您希望验证它们以获取Config
您知道有效的“新”实例。这是完全可能的,在某些情况下是合理validateConfig
的,如果你被困在那个不完美的世界里,你的方法是解决这个问题的完全合法的方法。
但是,缺点是编译器无法跟踪已验证Config
实例和尚未验证实例之间的差异。你的程序中会出现一些Config
实例,如果你想知道它们是否已经过验证,你必须追踪它们可能来自的所有地方。在某些情况下,这可能很好,但对于大型或复杂的程序,这并不理想。
总结一下:理想情况下,您会Config
在创建实例时验证实例(甚至可能无法创建无效的实例),这样您就不必记住任何给定Config
的是否好——类型系统可以为您记住. 如果这是不可能的,例如由于您无法控制的 API 或定义,或者对于简单的用例来说似乎过于繁琐,那么您正在做的事情validateConfig
是完全合理的。
作为一个脚注,既然您在上面说过您有兴趣在 Refined 上查看更多细节,那么它在这种情况下为您提供的是一种避免 shape 更多功能的方法A => ValidationResult[A]
。validateName
例如,现在您的方法可能需要 aString
并返回 a ValidationResult[String]
。你可以对这个签名提出完全相同的论点,就像我在Config => ValidationResult[Config]
上面反对的那样——一旦你处理结果(通过将函数映射到Validated
或其他),你只有一个字符串,而类型不会告诉你它已经过验证。
Refined 允许您做的是编写这样的方法:
def validateName(in: String): ValidationResult[Refined[String, SomeProperty]] = ...
... whereSomeProperty
可能指定最小长度,或者字符串与特定正则表达式匹配的事实等。重要的一点是,您没有验证 aString
并返回String
只有您知道的 a ——您正在验证 aString
并返回a编译器String
知道一些事情(通过包装器)。Refined[A, Prop]
同样,对于您的用例来说,这可能(好吧,可能是)过大了——您可能会发现很高兴知道您可以将这个原则(跟踪类型的验证)推到程序中更进一步。
推荐阅读
- ajax - 即使使用 SameSite=none,Cookie 也不会附加到请求
- amazon-web-services - 为什么 AWS Step Functions 转义了我的任务的 JSON 输出?
- python - 将 Series 值设置为 0 的最安全和最快的方法
- angular - 使用组件变量值更新快照
- diskspace - 多通道日志 (multipassd.log) 膨胀磁盘
- reactjs - 如何在 React 中使用 Font Awesome Icon?
- visual-studio - “无法连接到‘IIS Express’。” 我该如何解决?
- c# - 如何在程序仍在运行时按顺序运行 C# 代码并在视图中显示一些数据
- android - 我如何使用 2 个扩展 Work() 类的类
- ios - 获取 Storekit 产品时未出现 Apple 登录对话框