Composable data validation with error accumulation.
It shows how you might validate form data for a user.
type alias User =
{ name : Name
, email : Email
, age : Age
, password : Password
}
type alias UserForm =
{ name : String
, email : String
, age : String
, password : String
}
toUser : UserForm -> Validation String User
toUser { name, email, age, password } =
V.map4 User
(validateName name)
(validateEmail email)
(validateAge age)
(validatePassword password)
validateName : String -> Validation String Name
validateName =
Name.fromString
>> V.fromMaybe "A name is required"
validateEmail : String -> Validation String Email
validateEmail =
Email.fromString
>> Result.mapError
(\e ->
case e of
Email.Required ->
"An email is required"
Email.Invalid s ->
"\"" ++ s ++ "\"" ++ " is not a valid email"
)
>> V.fromResult
validateAge : String -> Validation String Age
validateAge =
Age.fromString
>> Result.mapError
(\e ->
case e of
Age.Required ->
"An age is required"
Age.InvalidString s ->
"\"" ++ s ++ "\"" ++ " is not a positive integer"
Age.InvalidInt n ->
String.fromInt n ++ " is not a valid age"
Age.TooYoung n ->
"You're too young: " ++ String.fromInt n
Age.TooOld n ->
"You're too old: " ++ String.fromInt n
)
>> V.fromResult
validatePassword : String -> Validation String Password
validatePassword =
Password.fromString
>> Result.mapError
(\e ->
case e of
Password.Required ->
"A password is required"
Password.TooShort n ->
"The password must be at least 8 characters: " ++ String.fromInt n
Password.TooLong n ->
"The password must be at most 20 characters: " ++ String.fromInt n
Password.MissingRequiredChars ->
"The password MUST contain at least one of each of the following: lower case ASCII character, upper case ASCII character, digit, and a special character ($ or %)"
)
>> V.fromResultYou can find the full source code for the example in the example/ directory.
validateName "" == fail "A name is required"
validateName "Dave MacQueen"
-- Success (Name "Dave MacQueen")validateEmail "" == fail "An email is required"
validateEmail "cs.uchicago.edu" == fail "\"cs.uchicago.edu\" is not a valid email"
validateEmail "macqueen@cs.uchicago.edu"
-- Success (Email "macqueen@cs.uchicago.edu")validateAge "" == fail "An age is required"
validateAge "forty" == fail "\"forty\" is not a positive integer"
validateAge "-1" == fail "-1 is not a valid age"
validateAge "10" == fail "You're too young: 10"
validateAge "65" == fail "You're too old: 65"
validateAge "49"
-- Success (Age 49)validatePassword "" == fail "A password is required"
validatePassword "1234567" == fail "The password must be at least 8 characters: 7"
validatePassword "123456789012345678901" == fail "The password must be at most 20 characters: 21"
validatePassword "12345678" == fail "The password MUST contain at least one of each of the following: lower case ASCII character, upper case ASCII character, digit, and a special character ($ or %)"
validatePassword "12345aB$"
-- Success (Password "12345aB$")toUser
{ name = ""
, email = "macqueen@cs.uchicago.edu"
, age = "10"
, password = "1234567"
}
== failWithErrors "A name is required" [ "You're too young: 10", "The password must be at least 8 characters: 7" ]A high-level overview of the public API.
type Validation
-- Construct
succeed : a -> Validation e a
fail : e -> Validation e a
failWithErrors : e -> List e -> Validation e a
fromResult : Result e a -> Validation e a
fromMaybe : e -> Maybe a -> Validation e a
-- Query
isValid : Validation e a -> Bool
isInvalid : Validation e a -> Bool
-- Map
map : (a -> value) -> Validation x a -> Validation x value
map2 : (a -> b -> value) -> Validation x a -> Validation x b -> Validation x value
map3 : (a -> b -> c -> value) -> Validation x a -> Validation x b -> Validation x c -> Validation x value
map4 : (a -> b -> c -> d -> value) -> Validation x a -> Validation x b -> Validation x c -> Validation x d -> Validation x value
map5 : (a -> b -> c -> d -> e -> value) -> Validation x a -> Validation x b -> Validation x c -> Validation x d -> Validation x e -> Validation x value
mapError : (x -> y) -> Validation x a -> Validation y a
-- Apply
apply : Validation x a -> Validation x (a -> b) -> Validation x b
-- Chain
andThen : (a -> Validation x b) -> Validation x a -> Validation x b
-- Convert
withDefault : a -> Validation e a -> a
toResult : Validation e a -> Result (List e) a
toMaybe : Validation e a -> Maybe a
-- Handle Errors
firstError : Validation e a -> Maybe e
lastError : Validation e a -> Maybe e
allErrors : Validation e a -> List e