Hi all,<br><br>I was trying to solve a problem and by chance found texts on functional<br>dependencies and then on type families (papers, ghc docs).<br><br>Please, consider the example 03 of &quot;Understanding functional dependencies<br>
via Constraint Handling rules&quot; by Sulzmann, Duck, Peyton-Jones and Stuckey.<br><br>There we are defining a multiplication over different numeric types:<br>class Mul a b c | a b -&gt; c where<br>&nbsp;&nbsp; (*) :: a -&gt; b -&gt; c<br>
instance Mul Int Int Int where ...<br>instance Mul Int Float Float where ...<br><br>Ok, we could add<br>instance Mul Float Int Float where ...<br><br>but if we want to make everything work nicely together, Integer, Complex,<br>
Rational etc, there will be a lot of instances, especially if we have to give<br>both &quot;Float Int&quot; and &quot;Int Float&quot; instances.<br><br>And now the question on &quot;lattices of types&quot; (didn&#39;t know any better name<br>
to refer to the following):<br>Is the following possible?&nbsp; Or can something similar achieved with<br>type families (easily)?<br><br>type lattice T a b<br>-- Each of the following gives a &quot;&lt;&quot;-relation between types,<br>
-- &quot;upper&quot; gives a method, how we get the larger (2nd) from<br>-- the smaller (1st).<br>lattice instance T Int Integer where upper = fromIntegral<br>lattice instance T Int Float&nbsp;&nbsp; where upper = fromIntegral<br>lattice instance T Integer (Ratio Integer) where upper = ...<br>
lattice instance T (Ratio Integer) Double&nbsp; where ...<br>lattice instance T Float Double ...<br>lattice instance T Double (Complex Double) ...<br><br>-- e.g.&nbsp;&nbsp; Now the compiler can check that the top type is<br>-- unique and that there is a path from every type to the top type.<br>
-- If the compiler founds this not to be the case, an error is output.<br>-- But otherwise there can be types that are not comparable but<br>-- the common top is quaranteed to exist.<br><br>class Mul a b c where<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lattice T<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*) :: a -&gt; b -&gt; c<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*) x y = (upper x y x) * (upper x y y)<br>-- Now there is no need for the instances like.<br>instance Mul Int Float Float where ...<br>instance Mul Float (Ratio Integer) Double where ...<br>
<br>The compiler can generate those instances, because we have given<br>the &quot;upper&quot;-methods. There is always the top available.<br>Function<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*) x y = (upper x y x) * (upper x y y)<br>might could be replaced with<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (*) x y = x * y<br>because of the presence of lattice T and thus, the &quot;upper&quot;-function.<br><br><br>If this were possible, the benefits would be:<br>- No need to state &quot;Int Float&quot; and &quot;Float Int&quot; instances on cases<br>
&nbsp; where the operands do commute.<br>- No need to write a large number of instances if you have several<br>&nbsp; &quot;types on lattice&quot; (e.g. some more or less well defined relation).<br>- If the relation between types is not clear, we could define another<br>
&nbsp; lattice T2 to our Mul2 class for the other uses.<br><br>Continuing the idea: we could override the default instances generated<br>by the compiler with our own instances when needed.<br><br><br>Ok, so, is it possible to write something like that already?&nbsp; (I just<br>
wouldn&#39;t like write a lot of instances...)&nbsp; If not, would it be<br>possible to extend, say ghc, to work that way or are there too much<br>inconsistencies in the above presentation?<br><br>(Yes, where is the bottom. Why top and not bottom?&nbsp; Should it be<br>
possible to tell, which one to use, if both present?&nbsp; e.g.)<br><br>Thanks for reading this far!<br><br>-- <br>br,<br>Isto