<div dir="ltr">I'm emulating a "closed typeclass" using a closed type family:<br><div><b><font face="courier new, monospace"><br></font></b></div><div><div><b><font face="courier new, monospace">    {-# LANGUAGE ExistentialQuantification #-}</font></b></div><div><b><font face="courier new, monospace">    {-# LANGUAGE GADTs                     #-}</font></b></div><div><b><font face="courier new, monospace">    {-# LANGUAGE LambdaCase                #-}</font></b></div><div><b><font face="courier new, monospace">    {-# LANGUAGE TypeFamilies              #-}</font></b></div><div><b><font face="courier new, monospace"><br></font></b></div><div><b><font face="courier new, monospace">    import GHC.Exts (Constraint)</font></b></div><div><b><font face="courier new, monospace"><br></font></b></div><div><b><font face="courier new, monospace">    -- This is like</font></b></div><div><b><font face="courier new, monospace">    --</font></b></div><div><b><font face="courier new, monospace">    --   class Thing a<br>    --   instance Thing Int<br>    --</font></b></div><div><div><b><font face="courier new, monospace">    type family Thing a :: Constraint where</font></b></div><div><b><font face="courier new, monospace">      Thing Int = ()</font></b></div></div><div><b><font face="courier new, monospace"><br></font></b></div><div><b><font face="courier new, monospace">    data Foo = forall a. Thing a => Foo (Bar a)</font></b></div><div><b><font face="courier new, monospace"><br></font></b></div><div><b><font face="courier new, monospace">    data Bar a where</font></b></div><div><b><font face="courier new, monospace">      Bar1 :: Bar Int</font></b></div><div><b><font face="courier new, monospace">      Bar2 :: Bar Bool</font></b></div><div><br></div><div><b><font face="courier new, monospace">    inspectFoo :: Foo -> ()</font></b></div><div><b><font face="courier new, monospace">    inspectFoo = \case</font></b></div><div><b><font face="courier new, monospace">      Foo Bar1 -> ()</font></b></div><div><b><font face="courier new, monospace">      Foo Bar2 -> () -- This is required to suppress a warning</font></b></div></div><div><b><font face="courier new, monospace"><br></font></b></div><div><font face="arial, sans-serif">Here, it's not possible to create a Foo using "Foo Bar2", because there is no "Thing Bool" constraint in scope. Yet, when picking apart a Foo, I must still pattern match on this impossible data.</font></div><div><font face="arial, sans-serif"><br></font></div><div>Is there some smarter way to write this code?<br><br>I thought of using `constraints`, and adding a catch-all clause to Thing:<br><br><font face="courier new, monospace"><b>    type family Thing a :: Constraint where<br>      Thing Int = ()</b></font></div><div><b style="font-family: 'courier new', monospace;">      Thing a   = Bottom</b><br><br><font face="arial, sans-serif">This would at least allow users of a Foo to use some</font><br><br><b style="font-family: 'courier new', monospace;">    absurd :: Bottom => a</b><br><br><font face="arial, sans-serif">thing to discharge the Bottom constraint brought in scope by pattern matching:<br><br></font></div><div><font face="courier new, monospace" size="2"><b>    inspectFoo :: Foo -> ()<br>    inspectFoo = \case<br>      Foo Bar1 -> ()<br>      Foo Bar2 -> absurd bottom</b></font></div><div><font face="courier new, monospace" size="2"><b><br></b></font></div><div><font face="arial, sans-serif" size="2">but better still would be to avoid having to write these impossible cases.</font></div><div><font face="arial, sans-serif" size="2"><br></font></div><div><font face="arial, sans-serif" size="2">Thanks!</font></div></div>