[GHC] #10318: Cycles in class declaration (via superclasses) sometimes make sense.

GHC ghc-devs at haskell.org
Wed Apr 13 16:27:26 UTC 2016


#10318: Cycles in class declaration (via superclasses) sometimes make sense.
-------------------------------------+-------------------------------------
        Reporter:  ekmett            |                Owner:
            Type:  feature request   |               Status:  closed
        Priority:  normal            |            Milestone:  8.0.1
       Component:  Compiler (Type    |              Version:  7.10.1
  checker)                           |
      Resolution:  fixed             |             Keywords:
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  GHC rejects       |            Test Case:  indexed-
  valid program                      |  types/should_compile/T10318
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------
Description changed by bgamari:

@@ -4,2 +4,1 @@
- {{{
-
+ {{{#!hs
@@ -32,1 +31,1 @@
- {{{
+ {{{#!hs
@@ -58,1 +57,1 @@
- {{{
+ {{{#!hs

New description:

 I'd like to be able to say the following, to describe the notion of an
 integral domain in Haskell:

 {{{#!hs
 -- | Product of non-zero elements always non-zero.
 -- Every integral domain has a field of fractions.
 -- The field of fractions of any field is itself.
 class (Frac (Frac a) ~ Frac a, Fractional (Frac a), IntegralDomain (Frac
 a))
   => IntegralDomain a where
   type Frac a :: *
   embed :: a -> Frac a

 instance IntegralDomain Integer where
   type Frac Integer = Rational
   embed = fromInteger

 instance IntegralDomain Rational where
   type Frac Rational = Rational
   embed = id
 }}}

 But GHC gets scared when it sees the cyclic reference that
 `IntegralDomain` instances depend on an IntegralDomain superclass, which
 really is cyclic in the (Frac a) case here, and that is kind of the point.
 =)

 Right now the best approximation of the correct answer that I have for
 this situation is to lie and claim the constraint is weaker:

 {{{#!hs
 -- | Product of non-zero elements always non-zero
 class (Frac (Frac a) ~ Frac a, Fractional (Frac a)) =>
 AlmostIntegralDomain a where
   type Frac a :: *
   embed :: a -> Frac a

 class (AlmostIntegralDomain a, AlmostIntegralDomain (Frac a)) =>
 IntegralDomain a
 instance (AlmostIntegralDomain a, AlmostIntegralDomain (Frac a)) =>
 IntegralDomain a

 instance AlmostIntegralDomain Integer where
   type Frac Integer = Rational
   embed = fromInteger

 instance AlmostIntegralDomain Rational where
   type Frac Rational = Rational
   embed = id
 }}}

 Now the user is stuck defining a different class than the one they
 consume.

 Alternately, with `ConstraintKinds`, I can encode:

 {{{#!hs
 data Dict p where
   Dict :: p => Dict p

 class (Frac (Frac a) ~ Frac a, Fractional (Frac a)) => IntegralDomain a
 where
   type Frac a :: *
   embed :: a -> Frac a
   proofFracIsIntegral :: p a -> Dict (IntegralDomain (Frac a))
   default proofFracIsIntegral :: IntegralDomain (Frac a) => p a -> Dict
 (IntegralDomain (Frac a))
   proofFracIsIntegral _ = Dict
 }}}

 but now whenever I need to get from `IntegralDomain a`  to `IntegralDomain
 (Frac a)` I need to explicitly open the `proofFracIsIntegral` with a rats'
 nest of `ScopedTypeVariables`.

 It would be really really nice if I could get GHC to deal with this for me
 as I currently have a few thousand lines of code hacking around this
 limitation. =/

--

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10318#comment:17>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list