
#10318: Cycles in class declaration (via superclasses) sometimes make sense. -------------------------------------+------------------------------------- Reporter: ekmett | Owner: Type: feature | Status: new request | Milestone: Priority: normal | Version: 7.10.1 Component: Compiler | Operating System: Unknown/Multiple (Type checker) | Type of failure: GHC rejects Keywords: | valid program Architecture: | Blocked By: Unknown/Multiple | Related Tickets: Test Case: | Blocking: | Differential Revisions: | -------------------------------------+------------------------------------- I'd like to be able to say the following, to describe the notion of an integral domain in Haskell: {{{ -- | 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: {{{ -- | 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: {{{ 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 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler