
#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