Challenge 1: UserId and ProductId, Caught at Compile Time — Possible Solution ==================================================================== Main.hs: newtype UserId = UserId Int newtype ProductId = ProductId Int greetUser :: UserId -> String greetUser (UserId n) = "Hello, user #" ++ show n Broken attempt: main :: IO () main = putStrLn (greetUser (ProductId 42)) Representative compile error: Main.hs:7:28: error: * Couldn't match type 'ProductId' with 'UserId' Expected: UserId Actual: ProductId * In the first argument of 'greetUser', namely '(ProductId 42)' Fixed: main :: IO () main = putStrLn (greetUser (UserId 42)) Output: Hello, user #42 Explanation: UserId and ProductId both wrap the exact same underlying representation, Int -- at runtime, there's genuinely no difference in how the two are stored. But because they're declared as distinct newtypes, the compiler treats them as completely different types. greetUser's signature demands specifically a UserId, so passing a ProductId -- even though it's "just an Int underneath" -- is rejected at compile time, exactly the kind of ID mix-up the chapter's own tip- box names as a real, practical newtype use case. WHY THIS WORKS AS AN ANSWER ------------------------------ This reproduces the exact ID-mix-up scenario the chapter's own tip- box describes, showing the real compile error produced when two newtypes with identical underlying representations are accidentally swapped.