Just so that this is answered, my problem was that I had an algebraic type where some fields were in common between the different constructors but there was a couple fields that weren't shared would die in runtime if I tried to use them.
data Exercise =
BarbellExercise {
name :: String,
weight :: Int,
reps :: Int
} |
BodyWeightExercise {
name :: String,
reps :: Int
}
exer1 = BarbellExercise "Squats" 235 15
exer2 = BarbellExercise "Deadlifts" 265 15
exer3 = BodyWeightExercise "Pullups" 12
exer4 = BarbellExercise "Overhead Press" 85 15
workout = [exer1, exer2, exer3, exer4]
test = do
mapM_ displayExercise workout
where
displayExercise x = putStrLn $ "Exercise: " ++ (name x) ++ " You must perform " ++ (show $ reps x) ++ "@" ++ (show $ weight x)
This compiles but dies runtime if I make the mistake of using the weight function. Understandable mistake. When lenses uses template haskell to generate instances it notices this and changes its behavior to prevent a mistake. You could remove the field accessors but in my case most of the fields were the same between datatypes. Here's how I should have written the data type once I noticed the fields did not match up:
data Exercise =
BarbellExercise
String -- ^ name
Int -- ^ reps
Int -- ^ weight
|
BodyWeightExercise
String -- ^ name
Int -- reps
name :: Exercise -> String
name (BarbellExercise n _ _) = n
name (BodyWeightExercise n _) = n
reps :: Exercise -> Int
reps (BarbellExercise _ r _) = r
reps (BodyWeightExercise _ r) = r
By doing it this way, while it is a little less clean, the error are caught at compile time. By forcing me to write the functions myself I would notice any partial functions as I was writing them.
I do kind of wish ghc would have warned me. It seems like it would be really easy for it to detect such a thing.