【发布时间】:2017-08-04 15:17:14
【问题描述】:
Haskell 泛型的大多数示例围绕 :+: 和 :*: 类型/构造函数递归地进行少量计算。我似乎正在解决一个可能无法解决的问题。
我正在尝试编写一个 generic 验证函数,它采用具有相同形状的任意两条记录,并根据 recordB 中定义的验证函数验证 recordA 中的每个字段,以返回 same 形状 OR recordA 本身。
例子:
-- Some type synonyms for better readability
type Name = Text
type Age = Int
type Email = Text
type GeneralError = Text
type FieldError = Text
-- a polymorphic record to help preserve the shape of various records
data User n a e = User {name :: n, age :: a, email :: e}
-- the incoming value which has been parsed into the correct type
-- but still needs various values to be validated, eg length, format, etc
type UserInput = User Name Age Email
-- specifies the exact errors for each field
type UserError = User [FieldError] [FieldError] [FieldError]
-- specifies how to validate each field. the validator is being passed
-- the complete record along with the specific field to allow
-- validations that depends on the value of another field
type UserValidator = User
(UserInput -> Name -> Either ([GeneralError], [FieldError]) Name)
(UserInput -> Age -> Either ([GeneralError], [FieldError]) Age)
(UserInput -> Email -> Either ([GeneralError], [FieldError]) Email)
let (validationResult :: Either ([GeneralError], UserError) UserInput)
= genericValidation (i :: UserInput) (v :: UserValidator)
现在,围绕:*: 递归执行此操作可能不起作用的原因是,需要查看每个 验证函数的结果,然后决定返回值是否应该是@ 987654325@ 或Right UserInput。我们无法在第一个失败的验证函数上评估 Left 值。
有什么方法可以使用 Haskell 泛型编写这个 genericValidation 函数吗?
【问题讨论】:
-
我不明白为什么这不应该是递归可行的。当您不确定需要哪一个时,只需返回 both 可能的结果。 — 也就是说,我不认为“任何两个具有相同形状的记录”是您可以非常可靠地拥有的东西——给定记录如何转换为
:*:-nestings 是未定义的。虽然我不明白为什么编译器会选择为相同结构的记录提供不同结构的表示。 -
每个字段的错误是否真的需要出现在数据类型的不同插槽中,或者像
Map FieldName FieldError这样的东西是否可以接受?还有,为什么每个字段的验证函数都需要访问整个UserInput? -
@danidiaz 使用
Map表示错误将是我最后的手段。这将意味着类型安全性的巨大损失,并且与当前最先进的技术(消化函子)没有显着差异。验证函数需要访问整个用户输入,以允许一个字段上的验证逻辑取决于另一个字段的值的情况。例如。如果参与者类型是“儿童”,则年龄必须小于 10。 -
@leftroundabout 即使返回了两个可能的结果,我也无法理解通用机器,它将在正确的嵌套级别的两组结果之一之间进行选择。如果某些东西期望具有相同形状的记录,并且给它提供了不同形状的记录(由于程序员错误或编译器怪癖),它不会导致编译时错误(我想这是一个非常复杂的错误,但是尽管如此编译错误)
标签: validation haskell generics recursion