
antecedent :: Rule -> Expression antecedent r = case r of .. Gc{} -> let x = grspe r in r `seq` Tm r ..
This looks wrong. The idea was to replace Tm (grspe r) (where the selection expression is put into Tm unevaluated) with let x = grspe r in x `seq` Tm x ie, bind the selected expression to 'x', then force 'x' to be evaluated before putting it in 'Tm x'. Alternatively, Tm $! (grspe r) should do the same forcing. However, either variant will force evaluation of the selected field as well, not just evaluation of the selection. Consider this small example: data T x = T {field :: x} f (T x) = Just x g tx = Just (field tx) h tx = Just $! (field tx) The three functions 'f', 'g', and 'h' are different, as can be seen (in part) by evaluating *Main> Data.Maybe.isJust $ f (T undefined) True *Main> Data.Maybe.isJust $ g (T undefined) True *Main> Data.Maybe.isJust $ h (T undefined) *** Exception: Prelude.undefined f: passes the 'field' 'x' unchanged g: passes the whole record, wrapped in a selection h: passes the 'field', after evaluating it The extra wrapping in 'g' corresponds to where Simon suspects your difference in memory behaviour comes from (by the time the wrappers get evaluated, there are too many of them to fit on the stack). Neither 'g' nor 'h' are equivalent to 'f', but the equivalent of 'h' (forcing the record selection before putting it in another constructor) might fit your needs better than the equivalent of 'g'. hth, Claus
antecedent :: Rule -> Expression antecedent r = case r of .. Gc{} -> let x = grspe r in r `seq` Tm x ..
This is forcing 'r', not the selection 'x' from 'r'.