Challenge 2: Combining Three Independently-Validated Fields — Possible Solution ==================================================================== Main.hs: data Address = Address { street :: String, city :: String, zip_ :: String } deriving (Show) validateStreet :: String -> Maybe String validateStreet s = if not (null s) then Just s else Nothing validateCity :: String -> Maybe String validateCity c = if not (null c) then Just c else Nothing validateZip :: String -> Maybe String validateZip z = if length z == 5 then Just z else Nothing mkAddress :: String -> String -> String -> Maybe Address mkAddress s c z = Address <$> validateStreet s <*> validateCity c <*> validateZip z main :: IO () main = do print (mkAddress "123 Main St" "Springfield" "12345") -- all valid print (mkAddress "123 Main St" "Springfield" "123") -- bad zip Output: Just (Address {street = "123 Main St", city = "Springfield", zip_ = "12345"}) Nothing Explanation: Address <$> validateStreet s applies fmap first, producing a Maybe holding a PARTIALLY-applied Address constructor (still needing city and zip). Each subsequent <*> supplies one more independently- validated field. If ALL THREE validations succeed, the fully-applied Address ends up wrapped in Just. If ANY ONE of them returns Nothing -- here, the zip code "123" fails its length check -- the whole chain short-circuits to Nothing immediately, regardless of whether the other two fields were valid. WHY THIS WORKS AS AN ANSWER ------------------------------ This builds a genuinely three-field applicative chain (matching the chapter's own two-field Person example, extended by one field), testing both the fully-successful case and a case where exactly one field fails, confirming the whole result becomes Nothing.