[GHC] #10318: Cycles in class declaration (via superclasses) sometimes make sense.
GHC
ghc-devs at haskell.org
Fri Apr 17 01:56:48 UTC 2015
#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
More information about the ghc-tickets
mailing list