То, что у вас уже есть, в основном хорошо, просто нужно немного почистить:
- Суб-валидации должны быть определениями верхнего уровня, так как они довольно сложны. (Кстати, подписи типов в определениях предложений
where
обычно опускаются.)
- Отсутствие согласованного соглашения об именах
- Множество последовательностей
(++)
может стать ужасным - используйте concat
(или, возможно, unwords
) вместо
- Незначительные причуды форматирования (есть некоторые лишние скобки,
concat . map f
- это concatMap f
и т. Д.)
Произведение всего этого:
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
Я думаю, это довольно хорошо. Если вам не нравится нотация списка, вы можете использовать монаду Writer [ErrorMsg]
:
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]