Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validations in Haskell

Tags:

haskell

I have a few nested records that I need to validate, and I wonder what is an idiomatic Haskell way to do it.

To simplify:

data Record = Record {
  recordItemsA :: [ItemA],
  recordItemB :: ItemB
} deriving (Show)

data ItemA {
  itemAItemsC :: [ItemC]
} deriving (Show)

Requirements are:

  • Collect and return all validation errors
  • Some validations may be across items, e.g. ItemsA against ItemB
  • Strings are sufficient to represent errors

I currently have code that feels awkward:

type ErrorMsg = String

validate :: Record -> [ErrorMsg]
validate record =
  recordValidations ++ itemAValidations ++ itemBValidations
  where
    recordValidations :: [ErrorMsg]
    recordValidations = ensure (...) $
      "Invalid combination: " ++ (show $ recordItemsA record) ++ " and " ++ (show $ recordItemsB record)
    itemAValidations :: [ErrorMsg]
    itemAValidations = concat $ map validateItemA $ recordItemsA record
    validateItemA :: ItemA -> [ErrorMsg]
    validateItemA itemA = ensure (...) $
      "Invalid itemA: " ++ (show itemA)
    itemBValidations :: [ErrorMsg]
    itemBValidations = validateItemB $ recordItemB record
    validateItemB :: ItemB -> [ErroMsg]
    validateItemB itemB = ensure (...) $
      "Invalid itemB: " ++ (show itemB)

ensure :: Bool -> ErrorMsg -> [ErrorMsg]
ensure b msg = if b then [] else [msg]
like image 621
Skirmantas Kligys Avatar asked Jan 04 '12 03:01

Skirmantas Kligys


People also ask

What is a validation in PHP?

Form Validation is a necessary process before the data entered in the form is submitted to the database. This is done to avoid unnecessary errors. In PHP Form validation, the script checks for data in respective fields based on the rules set by the developer, and returns an error if it does not meet the requirements.

What is validation explain different types of validation in PHP?

Validation in PHP is the process where we check if the input information in the various fields in any form such as text, checkbox or radio button, etc, submitted by the end-user in the form is correct or not using HTML code.

Which is the functions are common functions use to validate data in PHP?

The server-side validation validates data in the web server using PHP. To validate data in PHP, you can use the filter_var() and filter_input() functions.


2 Answers

Read the 8 ways to report errors in Haskell article. For your particular case, as you need to collect all errors and not only the first one, the approach with Writer monad suggested by @ehird seems to fit best, but it's good to know other common approaches.

like image 22
nponeccop Avatar answered Nov 16 '22 00:11

nponeccop


What you have already is basically fine, it just needs some clean-up:

  • The sub-validations should be top-level definitions, as they're fairly involved. (By the way, type signatures on where clause definitions are usually omitted.)
  • Lack of consistent naming convention
  • Lots of (++)s in sequence can get ugly — use concat (or perhaps unwords) instead
  • Minor formatting quirks (there are some superfluous parentheses, concat . map f is concatMap f, etc.)

The product of all this:

validateRecord :: Record -> [ErrorMsg]
validateRecord record = concat
  [ ensure (...) . concat $
      [ "Invalid combination: ", show (recordItemsA record)
      , " and ", show (recordItemB record)
      ]
  , concatMap validateItemA $ recordItemsA record
  , validateItemB $ recordItemB record
  ]

validateItemA :: ItemA -> [ErrorMsg]
validateItemA itemA = ensure (...) $ "Invalid itemA: " ++ show itemA

validateItemB :: ItemB -> [ErrorMsg]
validateItemB itemB = ensure (...) $ "Invalid itemB: " ++ show itemB

I think that's pretty good. If you don't like the list notation, you can use the Writer [ErrorMsg] monad:

validateRecord :: Record -> Writer [ErrorMsg] ()
validateRecord record = do
  ensure (...) . concat $
    [ "Invalid combination: ", show (recordItemsA record)
    , " and ", show (recordItemB record)
    ]
  mapM_ validateItemA $ recordItemsA record
  validateItemB $ recordItemB record

validateItemA :: ItemA -> Writer [ErrorMsg] ()
validateItemA itemA = ensure (...) $ "Invalid itemA: " ++ show itemA

validateItemB :: ItemB -> Writer [ErrorMsg] ()
validateItemB itemB = ensure (...) $ "Invalid itemB: " ++ show itemB

ensure :: Bool -> ErrorMsg -> Writer [ErrorMsg] ()
ensure b msg = unless b $ tell [msg]
like image 192
ehird Avatar answered Nov 16 '22 01:11

ehird