So this was interesting, what you have there isn't actually quite what's done, the actual code after the inlining is
main = print
. (\(l :*: r) -> selName l ++ selName r)
. unM1
. unM1
. from
$ (undefined :: Thing)
However, changing \(l :*: r) -> selName l ++ selName r
to what you had doesn't crash. So the difference is clearly in this line. The obvious thought, that there's something bad about the right field is quickly disproved since \(l :*: r) -> r
still runs.
We can see that the only nonbottom results are of the form (\l :*: r -> ???)
where ??? is either l
or r
. Nothing else.
So let's take a look at the derived instance with -ddump-deriv
.
from (Thing g1_atM g2_atN) = M1 (M1 (M1 (K1 g1_atM) :*: M1 (K1 g2_atN)))
Notice that this is strict in the constructor. So we must not be strict in the result of from undefined
because the code will crash. So now we're kinda walking on a house of cards here, since forcing any part of this will crash our program. The interesting bit is that
-- The derived selectors
instance Selector S1_0_0Thing where
selName _ = "foo"
instance Selector S1_0_1Thing where
selName _ = "bar"
isn't strict in it's argument. So here's the catch, your code all compiles down to the constant "foo"
because selName
is constant, we don't use any of the previous computations; it's a compile time computation. However, if we do any sort of computation with l
and r
in that lambda, than when we use selName
or do anything to see the result, we force the lambda to run, but since l :*: r
is really bottom, we crash.
As a quick demonstration, this will crash
main = (`seq` putStrLn "Crashes")
. (\(l :*: r) -> ())
. unM1
. unM1
. from
$ (undefined :: Thing)
But this will not
main = (const $ putStrLn "Crashes")
. (\(l :*: r) -> ())
. unM1
. unM1
. from
$ (undefined :: Thing)
TLDR, just make each of the fields undefined, but the toplevel constructor shouldn't be bottom.