[Haskell-cafe] closed classes [was: Re: exceptions vs. Either]
Martin Sulzmann
sulzmann at comp.nus.edu.sg
Wed Aug 11 23:04:59 EDT 2004
Hi,
here's my take on closed classes versus kinds.
1) Closed classes:
Consider
class Foo x
instance Foo Int
instance Foo Bool
Assume we'd like to prevent that Foo has any other members besides Int
and Bool. Haskell follows the open-world assumption. Hence, Foo t is assumed to
be true for any type t unless otherwise stated.
Oleg suggests to use functional dependency style
improvement. Indeed, we'd like to state that
"Foo x improves to False for any x different from Int and Bool".
The question is how to enforce this condition?
I don't think you can by just relying on functional dependencies.
1a) Closed classes in Chameleon:
E.g., in Chameleon we can enumerate all cases which should yield False
rule Foo Char ==> False
rule Foo (a,b) ==> False
rule Foo (a->b) ==> False
...
But what happens if we define a new type such as data Erk = ...?
Well, we'll need to add a new improvement rule
rule Foo Erk ==> False
1b) Closed-world assumption:
Alternatively, we might want to abandon the open-world assumption
and switch to a Prolog-like closed world approach.
See section 4.2 in [1] and section 8.1 in [2] for a discussion.
Essentially, we impose that
Foo x improves to x=Int or x=Bool
We can close classes once and for all, however, we're in trouble in
case instance declarations are recursive (e.g., Simon's example
involves a recursive instance declaration!)
Ref:
[1] Object-Oriented Style Overloading for Haskell by Mark Shields and Simon Peyton Jones.
[2] A Theory of Overloading by Peter J. Stuckey and Martin Sulzmann
b) Kinds
There's certainly a connection between closed classes and kinds.
Assume we have a system that supports closed classes (either by
following (1a) or (1b)).
Then
kind K = T1 | T2
class C (a::K)
can be encoded by
closed class K x
instance K T1
instance K T2
-- T1 and T2 are the only members of K
class K a => C a
-- C t implies K t which yields failure if t differs from T1 or T2
Martin
Oleg wrote:
> Simon Peyton-Jones wrote:
>
> > kind HNat = HZero | HSucc HNat
> >
> > class HNatC (a::HNat)
> >
> > instance HNatC HZero
> > instance HNatC n => HNatC (HSucc n)
> >
> > There is no way to construct a value of type HZero, or (HSucc HZero);
> > these are simply phantom types. ... A merit of declaring a kind
> > is that the kind is closed -- the only types of kind HNat are HZero,
> > HSucc HZero, and so on. So the class doesn't need to be closed; no one
> > can add new instances to HNatC because they don't have any more types of
> > kind HNat.
>
> With the flag -fallow-overlapping-instances, could I still add an instance
> instance HNatC n => HNatC (HSucc (HSucc n))
> or
> instance HNatC (HSucc HZero)
> ??
>
>
> If I may I'd like to second the proposal for closed classes. In some
> sense, we already have them -- well, semi-closed. Functional
> dependencies is the way to close the world somewhat. If we have a
> class
> class Foo x y | x -> y
> instance Foo Int Bool
> we are positive there may not be any instance 'Foo Int <anything>'
> ever again, open world and -fallow-overlapping-instances
> notwithstanding. In fact, it is because we are so positive about that
> fact that we may conclude that "Foo Int x" implies x = Bool. At least
> in the case of functional dependencies, the way to close the world gives
> us type improvement rules. One might wonder if there are other ways
> to (semi-)close the world with similar nice properties.
More information about the Haskell-Cafe
mailing list