Straightforward validation monad. Convenient for validating web forms and APIs.
web
Go to file
Denis Redozubov 97fea97c40
Merge pull request #7 from ilyakooo0/cleaned-up-code-and-added-documentation
Cleaned up code and added documentation
2020-02-03 11:34:50 +03:00
src/Control/Monad Cleaned up code and added documentation 2020-02-03 11:26:35 +03:00
test Added doctests and compiler warnings 2020-01-27 16:27:59 +03:00
.travis.yml Reenabled tests 2020-01-27 16:27:59 +03:00
ChangeLog.md Version bump 2018-06-25 19:23:00 +03:00
LICENSE Initial release 2017-05-17 13:08:45 +03:00
README.md Added readme 2020-01-27 16:27:40 +03:00
Setup.hs Initial release 2017-05-17 13:08:45 +03:00
stack.yaml Updated resolver 2020-01-27 16:29:27 +03:00
validationt.cabal Added doctests and compiler warnings 2020-01-27 16:27:59 +03:00

Travis CI Badge

ValidationT

A very small validation data library.

Usage

Suppose you want to validate some data from the user. Say, that the password adheres to some rules.

You might do something like this:

validatePassword :: Monad m => String -> ValidationT [String] m ()
validatePassword password = do
  vLength password
  vAlpha password
  vNum password
  where
    vLength p = when (length p < 8)
      $ vWarning ["The password should be at least 8 characters long."]
    vAlpha p = unless (any isAlpha p)
      $ vWarning ["The password should contain at least one alphabetic character."]
    vNum p = unless (any isDigit p)
      $ vWarning ["The password should contain at least one numeric character."]

ValidationT e m a essentially just gathers the errors thrown by vWarning.

vWarning :: (Monad m, Monoid e) => e -> ValidationT e m ()

vWarning mappends the given e to the already collected errors. This is why the warnings are contained inside a list.

There is also vError. The only difference between vWarning and vError is that vError stops further execution (and further collection of errors and warnings).

You would use the validation like this:

main :: IO ()
main = do
  password <- getLine
  result <- runValidationTEither . validatePassword $ password
  putStrLn $ case result of
    Left errs -> unlines err
    Right () -> "You are fine."

You could, of course, do more complicated things like use vWarningL and vErrorL to add an error to a mempty structure, which gets mappended with other errors.

The library comes with a MonoidMap newtype wrapper around Map, which mappends the values themselves on conflict. This can be useful if you have a multiple points of failure and you want to distinguich between them -- validating a username and a password for example:

data Piece
  = Password
  | UserName
  deriving (Eq, Show, Ord)

validatePassword :: Monad m => String -> ValidationT (MonoidMap Piece [String]) m ()
validatePassword password = do
  vLength password
  vAlpha password
  vNum password
  where
    warning = mmSingleton UserName
    vLength p = when (length p < 8)
      $ warning ["The password should be at least 8 characters long."]
    vAlpha p = unless (any isAlpha p)
      $ warning ["The password should contain at least one alphabetic character."]
    vNum p = unless (any isDigit p)
      $ warning ["The password should contain at least one numeric character."]

(mmSingleton is a covenience initializer for MonoidMap.)