- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
Haskell 泛型的大多数示例都围绕 :+:
递归地进行少量计算。和 :*:
类型/构造函数。我似乎正在解决一个可能无法解决的问题。
我正在尝试编写一个通用验证函数,该函数采用具有相同形状的任意两条记录,并根据 recordB 中定义的验证函数验证 recordA 中的每个字段,以返回 的错误记录。同 形状或记录A本身。
例子:
-- 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)
:*:
周围递归地执行此操作可能行不通的是,需要查看每个验证函数的结果,然后决定返回值是否应该是
Left ([GeneralError], UserError)
或
Right UserInput
.我们无法评估为
Left
失败的第一个验证函数上的值。
genericValidation
函数使用 Haskell 泛型?
最佳答案
Now, the reason why doing this recursively around
:*:
might not work is, that one needs to look at the result of every validation function and then decide if the return value should be aLeft ([GeneralError], UserError)
or aRight UserInput
. We cannot evaluate to aLeft
value on the first validation function that fails.
Applicative
Either
的行为不是这种类型的唯一合理行为!正如您所说,例如,当您验证表单时,您希望返回发生的所有错误的集合,而不仅仅是第一个错误。所以这里的类型与
Either
结构相同但有一个不同的
Applicative
实例。
newtype Validation e a = Validation (Either e a) deriving Functor
instance Semigroup e => Applicative (Validation e) where
pure = Validation . pure
Validation (Right f) <*> Validation (Right x) = Validation (Right $ f x)
Validation (Left e1) <*> Validation (Left e2) = Validation (Left $ e1 <> e2)
Validation (Left e) <*> _ = Validation (Left e)
_ <*> Validation (Left e) = Validation (Left e)
Semigroup
组合的两个错误。实例 - 两个错误,对于两者的一些合适的概念。如果两个计算都成功,或者只有一个失败,那么
Validation
表现得像
Either
.所以它有点像
Either
的科学怪人混搭和
Writer
应用程序。
Applicative
法律,但我会把证据留给你。哦,还有
Validation
不能成为合法的
Monad
.
Identity
来恢复原始记录。仿函数。
data UserTemplate f = UserTemplate {
name :: f Name,
age :: f Age,
email :: f Email
}
type User = UserTemplate Identity
Validator
是一个接受
a
的函数并返回
a
或错误的幺半群总结。
newtype Validator e a = Validator { runValidator :: a -> Validation e a }
HTraversable
就像
Traversable
但是对于从类型构造函数类别到 Hask 的仿函数。 (在
a previous question of mine 中了解更多信息。)
class HFunctor t where
hmap :: (forall x. f x -> g x) -> t f -> t g
class HFunctor t => HTraversable t where
htraverse :: Applicative a => (forall x. f x -> Compose a g x) -> t f -> a (t g)
htraverse f = hsequence . hmap f
hsequence :: Applicative a => t (Compose a g) -> a (t g)
hsequence = htraverse id
HTraversable
相关的?
Traversable
Classic™ 允许您进行排序
Applicative
效果如
Validation
在同类容器(如列表)上。但是记录更像是一个异构容器:记录“包含”一堆字段,但每个字段都有自己的类型。
HTraversable
正是您需要对
Applicative
进行排序时使用的类对多态容器的操作。
zipWith
到这些异构容器。
class HZip t where
hzip :: (forall x. f x -> g x -> h x) -> t f -> t g -> t h
UserTemplate
的方式构建的记录是可遍历和可压缩的。 (事实上,它们通常是
HRepresentable
——一个类似的高阶概念
Representable
——这是一个非常有用的属性,尽管我不会在这里详述。)
instance HFunctor UserTemplate where
hmap f (UserTemplate n a e) = UserTemplate (f n) (f a) (f e)
instance HTraversable UserTemplate where
htraverse f (UserTemplate n a e) = UserTemplate <$>
getCompose (f n) <*>
getCompose (f a) <*>
getCompose (f e)
instance HZip UserTemplate where
hzip f (UserTemplate n1 a1 e1) (UserTemplate n2 a2 e2) = UserTemplate (f n1 n2) (f a1 a2) (f e1 e2)
Generic
或模板 Haskell 实现
HTraversable
和
HZip
对于适合此模式的任意记录。
Validator
s 代表每个字段,然后
hzip
这些
Validator
s 沿着要验证的对象。那么你可以
htraverse
结果得到一个
Validation
包含经过验证的对象。根据您的问题,此模式适用于逐字段验证。如果您需要查看多个字段来验证您的记录,则不能使用
hzip
(但当然你也不能使用
Generic
)。
type Validatable t = (HZip t, HTraversable t)
validate :: (Semigroup e, Validatable t) => t (Validator e) -> Validator e (t Identity)
validate t = Validator $ htraverse (Compose . fmap Identity) . hzip val t
where val v = runValidator v . runIdentity
User
基本上涉及选择幺半群错误并返回验证函数的记录。我在这里定义了一个
Monoid
为
UserError
提升幺半群
e
逐点遍历记录的每个字段。
type UserError e = UserTemplate (Const e)
instance Semigroup e => Semigroup (UserError e) where
x <> y = hzip (<>) x y
type UserValidator = Validator ([GeneralError], UserError [FieldError])
validateEmail :: UserInput -> UserValidator Email
validateEmail i = Validator v
where v e
| '@' `elem` toString e = pure e
| otherwise = Validation $ Left ([], UserTemplate [] [] [FieldError "missing @"])
validateName :: UserInput -> UserValidator Name
validateName = ...
validateAge :: UserInput -> UserValidator Age
validateAge = ...
userValidator :: UserInput -> UserValidator User
userValidator input = validate $ UserTemplate {
name = validateName input,
age = validateAge input,
email = validateEmail input
}
关于validation - 是否有必要以递归方式编写 Haskell 泛型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45510332/
我正在使用 this solution在二进制矩阵中找到与图像边界对齐的矩形。假设现在我想找到一个不与图像边框对齐的矩形,并且我不知道它的方向;找到它的最快方法是什么? 为了示例,让我们寻找一个仅包含
else: 行在这个 Python 程序中是否正确/必要? from random import randrange for n in range(10): r = randrange(0,1
在 TDPL 7.1.5.1 中讨论了将 Widget w2 分配给 w1 并且作者指出“将 w2 逐个字段分配给 w1 会将 w2.array 分配给 w1.array——一个简单的数组边界分配,而
我是一名优秀的程序员,十分优秀!