[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