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 "Understanding functional dependencies<br>
via Constraint Handling rules" 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 -> c where<br> (*) :: a -> b -> 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 "Float Int" and "Int Float" instances.<br><br>And now the question on "lattices of types" (didn't know any better name<br>
to refer to the following):<br>Is the following possible? Or can something similar achieved with<br>type families (easily)?<br><br>type lattice T a b<br>-- Each of the following gives a "<"-relation between types,<br>
-- "upper" 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 where upper = fromIntegral<br>lattice instance T Integer (Ratio Integer) where upper = ...<br>
lattice instance T (Ratio Integer) Double where ...<br>lattice instance T Float Double ...<br>lattice instance T Double (Complex Double) ...<br><br>-- e.g. 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> lattice T<br>
(*) :: a -> b -> c<br> (*) 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 "upper"-methods. There is always the top available.<br>Function<br> (*) x y = (upper x y x) * (upper x y y)<br>might could be replaced with<br>
(*) x y = x * y<br>because of the presence of lattice T and thus, the "upper"-function.<br><br><br>If this were possible, the benefits would be:<br>- No need to state "Int Float" and "Float Int" instances on cases<br>
where the operands do commute.<br>- No need to write a large number of instances if you have several<br> "types on lattice" (e.g. some more or less well defined relation).<br>- If the relation between types is not clear, we could define another<br>
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? (I just<br>
wouldn't like write a lot of instances...) 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? Should it be<br>
possible to tell, which one to use, if both present? e.g.)<br><br>Thanks for reading this far!<br><br>-- <br>br,<br>Isto