我有一些我需要验证的嵌套记录,我想知道什么是惯用的Haskell方法.
简化:
data Record = Record {
recordItemsA :: [ItemA],
recordItemB :: ItemB
} deriving (Show)
data ItemA {
itemAItemsC :: [ItemC]
} deriving (Show)
Run Code Online (Sandbox Code Playgroud)
要求是:
ItemsA
反对ItemB
String
s足以表示错误我目前的代码感觉很尴尬:
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]
Run Code Online (Sandbox Code Playgroud)
您已经拥有的基本上没问题,只需要进行一些清理:
where
子句定义上的类型签名通常被省略。)(++)
顺序中的很多s 可能会变得丑陋 - 使用concat
(或者可能unwords
)代替concat . map f
isconcatMap 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
Run Code Online (Sandbox Code Playgroud)
我认为这很好。如果你不喜欢列表符号,你可以使用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]
Run Code Online (Sandbox Code Playgroud)