, :*:, , , , Left ([GeneralError], UserError) Right UserInput, Left .
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
newtype: a Validator - , a a, .
newtype Validator e a = Validator { runValidator :: a -> Validation e a }
: HTraversable Traversable, Hask. ( .)
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 Template Haskell HTraversable HZip , .
, : Validator , HZip Validator , . 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
}
- - .