
Those on the Haskell' mailing list may have seen recent signs of activity on the Haskell' front. I thought I should clarify the current status, and update the community on our plans for Haskell'. The main sticking point in the design of Haskell' has been the type system: namely whether Haskell' should have Functional Dependencies or Type Families. This issue is still undecided, although the experts are hard at work on developing a stronger understanding of Type Families in particular. Nevertheless, the committee feels that we cannot have a Haskell' without some way to resolve ambiguities when using multi-parameter type classes, be it Functional Dependencies (FDs) or Type Families (TFs). For one thing, a great deal of existing code uses FDs or depends on code that does, so unless we adopt one of these extensions all this code will still exist outside of Haskell', and that is far from ideal. So we decided to proceed in two stages: - "Haskell' alpha" will be a complete language specification, including all the modifications and additions we want to make to the language *except* for FDs or TFs. - Haskell' will follow afterward, adding either FDs or TFs. The motivation for this two-stage approach is that we can make progress on all the other parts of the language without being blocked on the type system, we can start work on implementing Haskell' alpha in our compilers, users can start using the new standard, and we can gain some experience with using it in practice. On the process side of things, we're now tracking the status of all language design proposals on this page: http://hackage.haskell.org/trac/haskell-prime/wiki/Status and as usual all the Haskell' resources are on the wiki: http://hackage.haskell.org/trac/haskell-prime/wiki The committee have been discussing various proposals amongst ourselves over the past few weeks, with the goal of making as many concrete decisions as possible. However, the general policy is still for technical discussions to take place in public, as far as possible. Changes to the status page are sent to the public mailing list, and we welcome comments on any of the issues, regardless of status. Thanks for your patience :-) And rest assured that progress is being made! Cheers, Simon (and the Haskell' committee)

Simon Marlow wrote: Subject: DRAFT: Haskell' status update of course, that shouldn't have said "DRAFT". Cheers, Simon

I tried to see the discussion that led to class aliases being rejected as a proposal, but could not find links on the Wiki. In fact, in Trac (#101) that proposal is still a 'maybe', but with no updates. Is there a competing proposal that got accepted? [Without a mechanism like class aliases, breaking up Num into a hierarchy of proper mathematical concepts becomes too unwieldly to be realistic. This is a real stumbling block for anyone trying to use the class system to encode fine-grained mathematical concepts.] Jacques

I would guess that the reason is that it hasn't been implemented yet... ...still it does seem to be a real shame if nothing in this area can make it into Haskell' .... (and it's not just Num .... Monad inheriting from Functor etc) ...it's a lot less sexy than something like TF, but I think it'd add a huge amount of value from the pragmatic point of view. --Ben On 21 Apr 2008, at 19:40, Jacques Carette wrote:
I tried to see the discussion that led to class aliases being rejected as a proposal, but could not find links on the Wiki. In fact, in Trac (#101) that proposal is still a 'maybe', but with no updates. Is there a competing proposal that got accepted?
[Without a mechanism like class aliases, breaking up Num into a hierarchy of proper mathematical concepts becomes too unwieldly to be realistic. This is a real stumbling block for anyone trying to use the class system to encode fine-grained mathematical concepts.]
Jacques _______________________________________________ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime

Jacques Carette wrote:
I tried to see the discussion that led to class aliases being rejected as a proposal, but could not find links on the Wiki. In fact, in Trac (#101) that proposal is still a 'maybe', but with no updates. Is there a competing proposal that got accepted?
[Without a mechanism like class aliases, breaking up Num into a hierarchy of proper mathematical concepts becomes too unwieldly to be realistic. This is a real stumbling block for anyone trying to use the class system to encode fine-grained mathematical concepts.]
We want to get Haskell' done, and unfortunately that may mean rejecting some good proposals - in this case, class aliases is a fairly large feature that we don't have enough experience with yet, so it was dropped (I proposed dropping it recently to the committee, and no-one argued against). That's not to say it isn't worthwhile, and this certainly doesn't prevent it from being adopted in a future version of the standard. We'd like to see it implemented in GHC, too. BTW, by the end of this process I do want to make sure we've documented the rationale for all the decisions, so that future committees have a useful knowledge base to work from. Cheers, Simon

| I tried to see the discussion that led to class aliases being rejected | as a proposal, but could not find links on the Wiki. In fact, in Trac | (#101) that proposal is still a 'maybe', but with no updates. Is there | a competing proposal that got accepted? | | [Without a mechanism like class aliases, breaking up Num into a | hierarchy of proper mathematical concepts becomes too unwieldly to be | realistic. This is a real stumbling block for anyone trying to use the | class system to encode fine-grained mathematical concepts.] I think this is partly my fault for failing to implement the idea -- a full implementation is really a prerequisite for Haskell'. One reason I've failed to do so is because I have not come under enough pressure! If I thought there were dozens of people slavering at the chops for class aliases, that would put it more firmly on my radar. Ben is right to say that it's much easier than the type-function stuff, which is decidedly tricky. (Of course there nothing stopping someone else doing it, but GHC's typechecker is one of it's more richly-interconnected parts.) Perhaps you have all just been very polite, and waiting in quiet but unrequited hope. Is this the most up-to-date description of the proposal? http://repetae.net/recent/out/classalias.html I've just had another look, which threw up quite a few questions in my mind. I wonder what would be a good list to discuss it. Maybe this one is not bad, because it has people interested in Haskell innovation, regardless of whether it's a live Haskell' candidate? Simon

On Tue, Apr 22, 2008 at 08:33:53AM +0100, Simon Peyton-Jones wrote:
Is this the most up-to-date description of the proposal? http://repetae.net/recent/out/classalias.html
There were a few changes proposed in the discussion that followed my announcement that I wanted to make. The one I can remember now is getting rid of the 'alias' keyword since the equals sign unabiguously identifies it as an alias. I will dig through the archive to find the others..
I've just had another look, which threw up quite a few questions in my mind. I wonder what would be a good list to discuss it. Maybe this one is not bad, because it has people interested in Haskell innovation, regardless of whether it's a live Haskell' candidate?
Sounds good to me. John -- John Meacham - ⑆repetae.net⑆john⑈

On Tue, Apr 22, 2008 at 09:18:23PM -0700, John Meacham wrote:
On Tue, Apr 22, 2008 at 08:33:53AM +0100, Simon Peyton-Jones wrote:
Is this the most up-to-date description of the proposal? http://repetae.net/recent/out/classalias.html
There were a few changes proposed in the discussion that followed my announcement that I wanted to make. The one I can remember now is getting rid of the 'alias' keyword since the equals sign unabiguously identifies it as an alias. I will dig through the archive to find the others..
Hmm.. looking at it again I think "my atrocious grammar" will be added to my list of things to fix. :) John -- John Meacham - ⑆repetae.net⑆john⑈

Am Mittwoch, 23. April 2008 06:18 schrieb John Meacham:
On Tue, Apr 22, 2008 at 08:33:53AM +0100, Simon Peyton-Jones wrote:
Is this the most up-to-date description of the proposal? http://repetae.net/recent/out/classalias.html
There were a few changes proposed in the discussion that followed my announcement that I wanted to make. The one I can remember now is getting rid of the 'alias' keyword since the equals sign unabiguously identifies it as an alias. I will dig through the archive to find the others..
I also have some remark: Why not write
class Eq a => Num a = (Additive a, Multiplicative a)
instead of
class Num a = Eq a => (Additive a, Multiplicative a)
The former would better match the syntax for ordinary class declarations like:
class Eq a => Num a where
[…]
[…]
Best wishes, Wolfgang

On Thu, Apr 24, 2008 at 08:48:15PM +0200, Wolfgang Jeltsch wrote:
Am Mittwoch, 23. April 2008 06:18 schrieb John Meacham:
On Tue, Apr 22, 2008 at 08:33:53AM +0100, Simon Peyton-Jones wrote:
Is this the most up-to-date description of the proposal? http://repetae.net/recent/out/classalias.html
There were a few changes proposed in the discussion that followed my announcement that I wanted to make. The one I can remember now is getting rid of the 'alias' keyword since the equals sign unabiguously identifies it as an alias. I will dig through the archive to find the others..
I also have some remark: Why not write
class Eq a => Num a = (Additive a, Multiplicative a)
instead of
class Num a = Eq a => (Additive a, Multiplicative a)
Well, because you can think of 'Num a' as an alias for 'Eq a => (Additive a, Multiplicative a)', not that Eq is a superclass of Num which the class declaration syntax implies. John -- John Meacham - ⑆repetae.net⑆john⑈

Am Donnerstag, 24. April 2008 21:27 schrieb John Meacham:
On Thu, Apr 24, 2008 at 08:48:15PM +0200, Wolfgang Jeltsch wrote: […]
I also have some remark: Why not write
class Eq a => Num a = (Additive a, Multiplicative a)
instead of
class Num a = Eq a => (Additive a, Multiplicative a)
Well, because you can think of 'Num a' as an alias for 'Eq a => (Additive a, Multiplicative a)', not that Eq is a superclass of Num which the class declaration syntax implies.
Hmm, in what way is Num a an alias for Eq a => (Additive a, Multiplicative a)? You cannot write this:
square :: (Eq a => (Additive a, Multiplicative a)) => a -> a
I would say: “Under the condition that Eq a holds, Num a is an alias for (Additive a, Multiplicative a). And this seems to be perfectly expressed by my above proposal. Best wishes, Wolfgang

On Thu, Apr 24, 2008 at 10:21:03PM +0200, Wolfgang Jeltsch wrote:
Am Donnerstag, 24. April 2008 21:27 schrieb John Meacham:
On Thu, Apr 24, 2008 at 08:48:15PM +0200, Wolfgang Jeltsch wrote: […]
I also have some remark: Why not write
class Eq a => Num a = (Additive a, Multiplicative a)
instead of
class Num a = Eq a => (Additive a, Multiplicative a)
Well, because you can think of 'Num a' as an alias for 'Eq a => (Additive a, Multiplicative a)', not that Eq is a superclass of Num which the class declaration syntax implies.
Hmm, in what way is Num a an alias for Eq a => (Additive a, Multiplicative a)? You cannot write this:
square :: (Eq a => (Additive a, Multiplicative a)) => a -> a
I would say: “Under the condition that Eq a holds, Num a is an alias for (Additive a, Multiplicative a). And this seems to be perfectly expressed by my above proposal.
Hmm... I guess it depends on how you think about it. I tend to think about them in terms of what they are rewritten to rather than a proposition about classes. but perhaps that makes more sense. Will mull on it some.. John -- John Meacham - ⑆repetae.net⑆john⑈

Am Mittwoch, 23. April 2008 06:18 schrieb John Meacham:
On Tue, Apr 22, 2008 at 08:33:53AM +0100, Simon Peyton-Jones wrote:
Is this the most up-to-date description of the proposal? http://repetae.net/recent/out/classalias.html
There were a few changes proposed in the discussion that followed my announcement that I wanted to make. The one I can remember now is getting rid of the 'alias' keyword since the equals sign unabiguously identifies it as an alias. I will dig through the archive to find the others..
I've just had another look, which threw up quite a few questions in my mind. I wonder what would be a good list to discuss it. Maybe this one is not bad, because it has people interested in Haskell innovation, regardless of whether it's a live Haskell' candidate?
Sounds good to me.
John
By the way, what are your current thoughts about your supertyping proposal. At least, on http://repetae.net/recent/out/supertyping.html you say:
This functionality becomes even more necessary when faced with binary-only libraries and standard language features which cannot be easily rewritten or overridden without great effort.
This seems to be an advantage compared to the class alias library. On the other hand, it looks a bit weird to me that you can express a class relation in two different ways: A t => B t and B t <= A t. Best wishes, Wolfgang

Is this the most up-to-date description of the proposal? http://repetae.net/recent/out/classalias.html
what sounds nice about the class alias proposal is that it is pure sugar, at least to the extent that type aliases are, but the design principle behind it seems to be that there should be a separate class for each method (as in Clean?), and that any compound classes should really just be class aliases (made to look like compound classes by the sugar), so that rearranging compound classes comes down to defining more aliases for the same single-method base classes. since this looks like class equivalence plus namespace handling, i was wondering how far one could get without the proposed extension. this is slightly more difficult than the proposed translation (which splits compound aliases into their components, so that the alias class is always translated away), but it might still be of interest. consider the 'class alias FooBar a = (Foo a,Bar a)' example from the proposal page. we define FooBar and Foor/Bar in separate modules and use that for namespace management. - FooAndBar defines Foo and Bar, as well as a type X which is an instance of both - FooBar defines FooBar, implicit derivations of FooBar from Foo/Bar and vice-versa (the aliasing part), as well as a type Y which is an instance of FooBar FooBar also arranges for Y to be an instance of Foo/Bar, and for X to be an instance of FooBar, via the implicit derivations, but controlled by instances of How note the class 'How' and its instances, which ensure that any type class instance is either defined, or derived (in a unique, specified way), but never both. problems: (1) instance method definitions by qualified names are not permitted, leading to the confusing 'foo = foo' (cf separate thread) (2) overlapping instances, due to the derived instances; it seems this can be held in check by the use of 'How', at the expense of some extra parameters/contexts/ instances to control how each instance is defined/derived example session: *FooBar> foo (X 1) False *FooBar> bar 0 (X 1) [X 1] *FooBar> foo (Y 1) True *FooBar> bar 0 (Y 1) [Y 1,Y 1] *FooBar> FooAndBar.foo (X 1) False *FooBar> FooAndBar.foo (Y 1) True *FooBar> FooAndBar.bar 0 (X 1) [X 1] *FooBar> FooAndBar.bar 0 (Y 1) [Y 1,Y 1] *FooBar> :t foo foo :: (FooBar a how) => a -> Bool *FooBar> :t FooAndBar.foo FooAndBar.foo :: (Foo a how) => a -> Bool *FooBar> :t bar bar :: (FooBar a how) => Int -> a -> [a] *FooBar> :t FooAndBar.bar FooAndBar.bar :: (Bar b how) => Int -> b -> [b] i don't think i'd recommend this encoding style (it does not quite fullfill the criterion of simplicity!-), but there you are: class aliases encoded. hth, claus ps. (for the TF vs FD fans: replacing FD class 'How' with a TF doesn't seem to work; a bug?)

John OK here's a question about class alisas. You propose: class Foo a where foo :: a -> [a] foo x = [] class Bar a where bar :: a -> a bar x = [x] class alias FooBar a = (Foo a, Bar a) where foobar :: a -> a foobar x = x foo x = bar x I have a few minor questions about this that'd be worth clarifying on your main page (a) I assume you can add a method 'foobar' not declared in either Foo or Bar. Your very first example has this. But it's contradicted later when you say that "One can declare an instance of Num either by giving separate instances for Eq, Additive, Multiplicative" (b) And I assume that you don't need to repeat the type signatures for 'foo' and 'bar'. (c) I think you intend that you can override the default methods for foo and bar; and I have done so for method 'foo'. Question: how does the above differ from this? class (Foo a, Bar a) => FooBarSC a where foobar :: a -> a Here Foo, Bar are simply superclasses. From the point of view of a type signature there *no* difference: f :: (FooBarSC a) => ... gives access to all the methods of Foo and Bar. So what's the difference? Answer (I believe): when you give an instance of FooBar you give implementations for all methods of Foo, Bar, and FooBar. So the obvious question is: do we really need a new construct? Why not just use FooBarSC? Then we'd have to allow you to give implementations for superclass methods too: instance FooBarSC Int where foobar = ... foo = ... bar = ... I think I believe (like you) that this is a bad idea. The main reason is that it's a totally unclear whether, given a FooBarSC Int instance declaration, should there be an instance for (Foo Int), always, never, or optionally? However, I think you might want to articulate the reasons carefully, because we have two features that are really extremely close. To put it another way, you could imagine re-expressing your proposal like this: class (Eq a) && (Additive a, Multiplicative a) => Num a meaning this: when you give an instance for (FooBar T) you * MUST give implementations for the methods of Addititive and Applicative * MUST NOT give implementations for methods of Eq; rather the Eq T instance must be in scope. This is, I believe, what you mean by class alias Num a = Eq a => (Additive a, Multiplicative a) Now I'm not necessarily suggesting this as concrete syntax. But my point is that you're really asking for small modification of the existing superclass mechanism, that divides the superclasses into two groups, the "flat" ones (like Additive and Multiplicative) and the "nested" ones (like Eq). Is that right? If so, a syntax that is more suggestive of the current superclass declaration looks better to me. This close relationship also suggests strongly that the answer to (a) above should be 'yes', since you can certainly add methods to a class with superclasses. I won't say more until I'm sure I've understood your intent. Simon

On Fri, Apr 25, 2008 at 05:37:17PM +0100, Simon Peyton-Jones wrote:
John
OK here's a question about class alisas. You propose:
class Foo a where foo :: a -> [a] foo x = [] class Bar a where bar :: a -> a bar x = [x]
class alias FooBar a = (Foo a, Bar a) where foobar :: a -> a foobar x = x
foo x = bar x
I have a few minor questions about this that'd be worth clarifying on your main page (a) I assume you can add a method 'foobar' not declared in either Foo or Bar. Your very first example has this. But it's contradicted later when you say that "One can declare an instance of Num either by giving separate instances for Eq, Additive, Multiplicative"
No, I didn't mean to imply that. as you noted, that would mean that an alias would not be a simple preprocessing. what you can do is create a real class FooBar which has both foo and bar as superclasses, then create an alias for all three so you can create a single instance to define all three. It might be possible to add this though and have it automatically create an appropriate class and a class alias. but I think that might muddy the intent of class aliases if not all can be described as simple aliases for other existing classes. So we can reserve that as a possible future extension. it is easy enough to manually create the 'foobar' containing class in any case and if you don't export it from your module, you get the equivalent effect.
(b) And I assume that you don't need to repeat the type signatures for 'foo' and 'bar'.
Yup, no need to repeat type signatures.
(c) I think you intend that you can override the default methods for foo and bar; and I have done so for method 'foo'.
Yes. being able to override default instances is a key part, otherwise you wouldn't be able to, for instance, create appropriate 'Num' compatible methods from your advanced 'NewNum' class alias. But you are not forced to do so.
Question: how does the above differ from this?
class (Foo a, Bar a) => FooBarSC a where foobar :: a -> a
Here Foo, Bar are simply superclasses. From the point of view of a type signature there *no* difference:
f :: (FooBarSC a) => ...
gives access to all the methods of Foo and Bar. So what's the difference?
Answer (I believe): when you give an instance of FooBar you give implementations for all methods of Foo, Bar, and FooBar.
Yes, it is because you cannot declare an instance of FooBarSC and have it also create the ones for Foo and Bar, and because you can't create new default instances for 'foo' and 'bar' that refer to 'foobar'.
So the obvious question is: do we really need a new construct? Why not just use FooBarSC? Then we'd have to allow you to give implementations for superclass methods too: instance FooBarSC Int where foobar = ... foo = ... bar = ...
I think I believe (like you) that this is a bad idea. The main reason is that it's a totally unclear whether, given a FooBarSC Int instance declaration, should there be an instance for (Foo Int), always, never, or optionally?
Yes. and it breaks one of the major haskell properties I am not willing to give up, That when you add an import to your module (or any module you depend on), your program will either fail to compile or have the _exact_ same meaning.
However, I think you might want to articulate the reasons carefully, because we have two features that are really extremely close.
Yes, I think the above might help with the lattice case, where you have a strict hierarchy of classes and the superclass tree mirrors your class aliases, but this may not be the case in general. in particular, we don't want to necessarily redo the numeric hierarchy as a strict generalization or splitting up of the current prelude numerical hierarchy, and the superclass method gets a little hairy when you want to do things like that and don't want to end up with every method in its own class.
To put it another way, you could imagine re-expressing your proposal like this:
class (Eq a) && (Additive a, Multiplicative a) => Num a
meaning this: when you give an instance for (FooBar T) you
* MUST give implementations for the methods of Addititive and Applicative
* MUST NOT give implementations for methods of Eq; rather the Eq T instance must be in scope.
This is, I believe, what you mean by class alias Num a = Eq a => (Additive a, Multiplicative a)
Now I'm not necessarily suggesting this as concrete syntax. But my point is that you're really asking for small modification of the existing superclass mechanism, that divides the superclasses into two groups, the "flat" ones (like Additive and Multiplicative) and the "nested" ones (like Eq). Is that right? If so, a syntax that is more suggestive of the current superclass declaration looks better to me.
Hmm.. I consider class aliases orthogonal and complementary to superclasses, rather than a generalization of them. I do not think that what one wants out of a superclass hierarchy and a class alias collection (note, I don't say hierarchy) will be the same and having them mirror each other will create a tension between the two in how one designs their code.
This close relationship also suggests strongly that the answer to (a) above should be 'yes', since you can certainly add methods to a class with superclasses.
Yeah, I disagree here, mainly because I don't want to conflate superclasses with class aliases. I feel they have different uses, even though they can sometimes achieve the same thing. Part of this is my 'sather' heritige, sather was a pretty cool fully type safe (in the stronger FP sense moreso than the java sense) object oriented language that was somewhat based on eiffel, but with the advantage of being designed by people with a stronger grasp of type theory than the eiffel designer. One of its major innovations was a separation of code reuse from class inheritance. In traditional object oriented languages, inheritance is both a method of code reuse (by defining non abstract superclasses with virtual methods that can be overridden), and of expressing a strong relationship between the types. However the sather designers realized these two things were not necessarily related, their solution was to define superclasses as _always_ being abstract, and having a different mechanism for code reuse. It was quite nice. enough so that I started (not as elegantly) using that approach in other OO languages I was using. Had I not discovered Haskell, I would likely still be the sather language maintainer :) I believe that class aliases define a method of code (instance method in particular) reuse. When you define your 'HaskellPrimePrelude' instances, you want the code to automatically and transparently be used to define 'Haskell98Prelude' instances, likewise, when you define 'Haskell98Prelude' instances, you want the code to automatically and transparently define 'HaskellPrimePrelude' instances. However, there is no clear superclass relationship between the two and being forced to introduce an artificial one would obfuscate things I think. John -- John Meacham - ⑆repetae.net⑆john⑈

| Yeah, I disagree here, mainly because I don't want to conflate | superclasses with class aliases. I feel they have different uses, even | though they can sometimes achieve the same thing. Fair enough. But the strange syntax class alias Num a = Eq a => (Additive a, Multiplicative a) *does* seem so say that the (Eq a) behaves in a superclass way, and (Additive a, Multiplicative a) behave in a class-alias way, as it were. That seems inconsistent with the design goal you describe above. Perhaps I can put it like this: if you want the "(Eq a) =>" part of the above decl, then you should also allow method definitions in the same decl. Once this point settles down, would you like to refine your specification in the light of my misunderstandings? The more precise it is, the easier it is to implement. Concerning Twan's idea, my brain is too small to accommodate the similarities and differences (today at any rate). But from an implementer's point of view, it'd be nice to have one proposal on the table rather than two competing ones.... Simon

On Thu, May 01, 2008 at 03:42:53PM +0100, Simon Peyton-Jones wrote:
| Yeah, I disagree here, mainly because I don't want to conflate | superclasses with class aliases. I feel they have different uses, even | though they can sometimes achieve the same thing.
Fair enough. But the strange syntax
class alias Num a = Eq a => (Additive a, Multiplicative a)
*does* seem so say that the (Eq a) behaves in a superclass way, and (Additive a, Multiplicative a) behave in a class-alias way, as it were. That seems inconsistent with the design goal you describe above.
Wolfgang suggested the alternate syntax class alias Eq a => Num a = (Additive a, Multiplicative a) where .... The correct reading being: if 'Eq a' then 'Num a' is an alias for (Additive a,Multiplicative a) I think I am coming around to his point of view, do you think this makes it clearer? John -- John Meacham - ⑆repetae.net⑆john⑈

| > Fair enough. But the strange syntax | > | > class alias Num a = Eq a => (Additive a, Multiplicative a) | > | > *does* seem so say that the (Eq a) behaves in a superclass way, and | > (Additive a, Multiplicative a) behave in a class-alias way, as it | > were. That seems inconsistent with the design goal you describe | > above. | | Wolfgang suggested the alternate syntax | | class alias Eq a => Num a = (Additive a, Multiplicative a) where .... | | The correct reading being: | | if 'Eq a' then 'Num a' is an alias for (Additive a,Multiplicative a) | | I think I am coming around to his point of view, do you think this makes | it clearer? I am not arguing about syntax! You say "class aliases are orthogonal to superclasses", but then you allow this "Eq a" thing in the above alias, which is very like a superclass. I think that if you allow the "Eq a =>" part, you should also allow new methods to be declared in the alias (as I originally thought you did). And if not, then you shouldn't allow superclasses. It's precisely that you allow superclasses (Eq a =>) that makes your feature non-orthogonal to ordinary superclasses. Maybe you can't make them orthogonal, but it quite hard to explain this definition to me. Incidentally, you say that your proposal is just syntactic sugar: if so, can you give the desugaring translation? Simon

On Thu, May 01, 2008 at 11:57:14PM +0100, Simon Peyton-Jones wrote:
| > Fair enough. But the strange syntax | > | > class alias Num a = Eq a => (Additive a, Multiplicative a) | > | > *does* seem so say that the (Eq a) behaves in a superclass way, and | > (Additive a, Multiplicative a) behave in a class-alias way, as it | > were. That seems inconsistent with the design goal you describe | > above. | | Wolfgang suggested the alternate syntax | | class alias Eq a => Num a = (Additive a, Multiplicative a) where .... | | The correct reading being: | | if 'Eq a' then 'Num a' is an alias for (Additive a,Multiplicative a) | | I think I am coming around to his point of view, do you think this makes | it clearer?
I am not arguing about syntax!
oh, I just meant that this syntax is actually a different way of thinking about it for me and it helped me clarify some stuff in my mind so thought it might be clearer for others as well.
You say "class aliases are orthogonal to superclasses", but then you allow this "Eq a" thing in the above alias, which is very like a superclass. I think that if you allow the "Eq a =>" part, you should also allow new methods to be declared in the alias (as I originally thought you did). And if not, then you shouldn't allow superclasses. It's precisely that you allow superclasses (Eq a =>) that makes your feature non-orthogonal to ordinary superclasses. Maybe you can't make them orthogonal, but it quite hard to explain this definition to me.
Oh, the reason to allow superclasses of class aliases is so methods of the superclass can be used in the default instance methods for your alias. In addition, it allows full emulation of any explicit class you can currently declare. You do not want 'instance 'Num a'' to declare an instance for Eq as that is very different behavior from the old Num class. Yet the default instances for 'Num' may require use of methods from its superclass. it actually would make more sense to call them the context of the class alias rather than the superclass, since they don't declare a super/sub class relationship between the two.
Incidentally, you say that your proposal is just syntactic sugar: if so, can you give the desugaring translation?
Hmm.. okay, here is a rough draft that covers all the important cases I think. assume the following declarations:
class C1 a where f1 :: t1 f1 = d1
class C2 a where f2 :: t2 f2 = d2 f3 :: t3 f3 = d3
class alias S a => A a = (C1 a, C2 a) where f1 = nd1
okay, the desugaring is as follows: there are two different desugaring rules, one for instances, one for the alias appearing anywhere other than an instance declaration:
g :: A a => a -> b g = ...
translates to
g :: (S a, C1 a, C2 a) => a -> b g = ...
the triplet of (S a, C1 a, C2 a) is completely equivalent to (A a) in all ways and all places (other than instance heads), one is just a different way to express the other, just like type synonyms. An alias just expands to the union of the classes it is an alias for as well as its class constraints (superclasses). now for instance declarations
instance A a where f2 = bf2
expands to
instance (S a) => C1 a where f1 = nd1
instance (S a) => C2 a where f2 = bf2 f3 = d3
Note that when declaring an instance of a concrete type, like Int, the constraint (S Int) will be trivially satisfied or not at compile time. (bf2 is free to use methods of 'S' of course). this translation is also a bijection, declaring those two instances manually as above is indistinguishable from declaring instances via the alias in all ways. Hopefully the generalization to arbitrary numbers of classes is clear... John -- John Meacham - ⑆repetae.net⑆john⑈

Hmm.. okay, here is a rough draft that covers all the important cases I think.
assume the following declarations:
class C1 a where f1 :: t1 f1 = d1
class C2 a where f2 :: t2 f2 = d2 f3 :: t3 f3 = d3
class alias S a => A a = (C1 a, C2 a) where f1 = nd1
okay, the desugaring is as follows:
there are two different desugaring rules, one for instances, one for the alias appearing anywhere other than an instance declaration:
g :: A a => a -> b g = ...
translates to
g :: (S a, C1 a, C2 a) => a -> b g = ...
the triplet of (S a, C1 a, C2 a) is completely equivalent to (A a) in all ways and all places (other than instance heads), one is just a different way to express the other, just like type synonyms. An alias just expands to the union of the classes it is an alias for as well as its class constraints (superclasses).
now for instance declarations
instance A a where f2 = bf2
expands to
instance (S a) => C1 a where f1 = nd1
instance (S a) => C2 a where f2 = bf2 f3 = d3
Note that when declaring an instance of a concrete type, like Int, the constraint (S Int) will be trivially satisfied or not at compile time. (bf2 is free to use methods of 'S' of course).
this translation is also a bijection, declaring those two instances manually as above is indistinguishable from declaring instances via the alias in all ways.
Hopefully the generalization to arbitrary numbers of classes is clear...
What about multiple parameters? Can A have more parameters than the Ci? Should they be in the same order? Should they overlap? What about instance contexts, like: instance I a => A a where ... (What about functional dependencies?) Tom -- Tom Schrijvers Department of Computer Science K.U. Leuven Celestijnenlaan 200A B-3001 Heverlee Belgium tel: +32 16 327544 e-mail: tom.schrijvers@cs.kuleuven.be url: http://www.cs.kuleuven.be/~toms/

On Fri, May 02, 2008 at 08:30:59AM +0200, Tom Schrijvers wrote:
Note that when declaring an instance of a concrete type, like Int, the constraint (S Int) will be trivially satisfied or not at compile time. (bf2 is free to use methods of 'S' of course).
this translation is also a bijection, declaring those two instances manually as above is indistinguishable from declaring instances via the alias in all ways.
Hopefully the generalization to arbitrary numbers of classes is clear...
What about multiple parameters? Can A have more parameters than the Ci? Should they be in the same order? Should they overlap?
What about instance contexts, like:
instance I a => A a where ...
Ah, I originally had instance contexts in my example, but left them out for clarity of the main points. instance contexts are just copied verbatim into each expanded instance.
(What about functional dependencies?)
I am leaving out MPTCs and hence fundeps for now, I do not believe they will present an issue, as nothing about the expansion depends on the number of arguments, but I want to make sure we have a clear understanding of what class aliases imply for haskell 98 one constructor type classes first. I think the extension to MPTCs is clear, not so clear for fundeps but not unsolvable, then again, nothing much is clear about the future of fundeps so I don't think that is a big issue. John -- John Meacham - ⑆repetae.net⑆john⑈

John This is good stuff, but I fear that in 3 months time it'll be buried in our email archives. In contrast, your original web page is alive and well, and we were able to start our discussion based on it So can I suggest that you transfer your web page to the Haskell' wiki (simply a convenient, editable place to develop it), or to the haskell.org wiki (likewise). And that, as the design gets fleshed out, you try to reflect the current state of play there? I don't want this work to be lost! Ok, on to your email: =============== Desugaring the class alias decl ================= | there are two different desugaring rules, one for instances, one for the | alias appearing anywhere other than an instance declaration: | | > g :: A a => a -> b | > g = ... | | translates to | | > g :: (S a, C1 a, C2 a) => a -> b | > g = ... | | the triplet of (S a, C1 a, C2 a) is completely equivalent to (A a) in | all ways and all places (other than instance heads) Notice that this part *is* exactly true of a superclass with no methods class (S a, C1 a, C2 a) => A a where {} That's not necessarily bad; but it does make it harder to figure out when to user a superclass and when to use a class alias. Does that make sense? In fact, I suggest the following (**): the class alias
class alias S a => A a = (C1 a, C2 a) where f1 = nd1
desugars to class (S a, C1 a, C2 a) => A a The class alias decl *also* affects the desugaring of instances, still to come, but by desugaring the class alias into an ordinary class, you don't have to say *anything* about g :: (S a, C1 a, C2 a) => a -> b vs g :: (A a) => a -> b =============== Desugaring instanc decls ================= | now for instance declarations | | > instance A a where | > f2 = bf2 | | expands to | | > instance (S a) => C1 a where | > f1 = nd1 | | > instance (S a) => C2 a where | > f2 = bf2 | > f3 = d3 Do you really mean that? Presumably 'a' is not a type variable here? Furthermore, instance decls typically have a context. Unless I have profoundly misunderstood, I think you mean this: instance (Foo a, Bar b) => A (a,b) where f1 = bf1 expands to instance (Foo a, Bar b) => C1 (a,b) where f1 = nd1 instance (Foo a, Bar b) => C2 (a,b) where f2 = bf2 f2 = d3 Notice the *absence* of an instance for (S (a,b)). It's up to the *user* to ensure that there is such an instance, perhaps, say instance Foo a => S (a,b) where ... In this way S is behaving just like any ordinary superclass. If we have class S a => T a then given an instance instance (Foo a, Bar b) => T (a,b) it's up to the user to ensure that there is an instance for S (a,b). With the desugaring (**) I proposed above, we'd add one more instance: instance (Foo a, Bar b) => A (a,b) | Hopefully the generalization to arbitrary numbers of classes is clear... I'm not sure either way. Let's get this written up first. Simon

This isn't really a response to your email, but I have been mulling the last few hours away from a computer and wanted to get this stream of conciousness out when it is fresh in my mind. The more I think about it, I think 'superclass' is just the wrong terminology for dealing with class aliases. Superclass implies a strict partial order on classes, which just isn't the case for class aliases, for instance
class alias Foo a => Foo a = Bar a where ...
Has a defined (if not very useful) meaning with class aliases, but is really odd if you consider 'Foo a' a "superclass". So, I think the following terminology should be used: Context of --+ alias | The alias -+ +--- The expansion of the alias | | | v v v
class alias (S1 a .. Sn a) => A a = (C1 a ... Cn a) where fd1 = .... .... fdn = .... ^ +---- The defaults of the alias
given this, the expansion of 'A a' in any context other than an instance head is
A a --> reduce(S1 a .. Sn a, C1 a ... Cn a)
where reduce is standard entailment reduction on class contexts (like (Eq a,Ord a, Eq a) reduces to (Ord a)) This expansion is carried out iteratively on all class aliases until a fixed point is reached, then all class aliases are deleted from the result and the remaining context is the final result. (This will always terminate due to there being a finite number of class aliases that can be pulled into the expansion) likewise, for instance declarations:
instance A a where ...
-->
foreach C in C1 .. Cn: instance (S1 a ... Sn a) => C a where ...
I left out the default methods here. I need to think about them a bit more to come up with a formal expansion as it is a bit trickier (to typeset if nothing else), but I hope this is somewhat more clear for some... John -- John Meacham - ⑆repetae.net⑆john⑈

Any chance to express this in terms of a formal (constraint rewrite framework). For example, the Haskell rule, do *not* display implied superclasses, can be specified as follows. Consider the special case of class Eq a class Eq a => Ord a Eq a, Ord a <=> Ord a The above rule only applies *after* type inference took place. Martin John Meacham wrote:
This isn't really a response to your email, but I have been mulling the last few hours away from a computer and wanted to get this stream of conciousness out when it is fresh in my mind.
The more I think about it, I think 'superclass' is just the wrong terminology for dealing with class aliases. Superclass implies a strict partial order on classes, which just isn't the case for class aliases, for instance
class alias Foo a => Foo a = Bar a where ...
Has a defined (if not very useful) meaning with class aliases, but is really odd if you consider 'Foo a' a "superclass". So, I think the following terminology should be used:
Context of --+ alias | The alias -+ +--- The expansion of the alias | | | v v v
class alias (S1 a .. Sn a) => A a = (C1 a ... Cn a) where fd1 = .... .... fdn = ....
^ +---- The defaults of the alias
given this, the expansion of 'A a' in any context other than an instance head is
A a --> reduce(S1 a .. Sn a, C1 a ... Cn a)
where reduce is standard entailment reduction on class contexts (like (Eq a,Ord a, Eq a) reduces to (Ord a))
This expansion is carried out iteratively on all class aliases until a fixed point is reached, then all class aliases are deleted from the result and the remaining context is the final result. (This will always terminate due to there being a finite number of class aliases that can be pulled into the expansion)
likewise, for instance declarations:
instance A a where ...
-->
foreach C in C1 .. Cn: instance (S1 a ... Sn a) => C a where ...
I left out the default methods here. I need to think about them a bit more to come up with a formal expansion as it is a bit trickier (to typeset if nothing else), but I hope this is somewhat more clear for some...
John

| The more I think about it, I think 'superclass' is just the wrong | terminology for dealing with class aliases. Superclass implies a strict | partial order on classes, which just isn't the case for class aliases, | for instance | | > class alias Foo a => Foo a = Bar a where ... Crumbs! I have no idea what that means! Did you really mean to repeat "Foo"? According to your expansion in type signatures f :: (Foo a) => ... expands to f :: (Foo a, Bar a) => ... which presumably expands again. I'm totally lost here Have a look at my last message, which gives a variant of your desugaring that IMHO greatly clarifies the meaning of (what I understand by) aliases. Simon

On Fri, May 02, 2008 at 11:24:11AM +0100, Simon Peyton-Jones wrote:
| The more I think about it, I think 'superclass' is just the wrong | terminology for dealing with class aliases. Superclass implies a strict | partial order on classes, which just isn't the case for class aliases, | for instance | | > class alias Foo a => Foo a = Bar a where ...
Crumbs! I have no idea what that means! Did you really mean to repeat "Foo"? According to your expansion in type signatures f :: (Foo a) => ... expands to f :: (Foo a, Bar a) => ... which presumably expands again. I'm totally lost here
Yes I did, because I wanted to make the differences between class alias contexts and superclasses very clear, the above context is valid, if vacuous. the expansion goes as follows . 1. Foo a --> reduce(Foo a,Bar a) -- Foo a expanded 2. reduce(Foo a,Bar a) --> (Foo a,Bar a) -- no entailment reduction possible, reduction is unchanged from H98 3. (Foo a,Bar a) -> reduce(Foo a,Bar a,Bar a) -- Foo a expanded 4. reduce(Foo a, Bar a, Bar a) -> (Foo a, Bar a) -- reductino removes duplicates 5. we notice we are the same as in step #2. fixed point reached, we stop expansion. 6. we remove all class aliases from result: (Foo a, Bar a) -> Bar a 7. 'Bar a' is our final result. informal proof of termination: each step adds a new class or class alias to the context, there are a finite number of classes or class aliases, therefore we must eventually reach a fixed point.
Have a look at my last message, which gives a variant of your desugaring that IMHO greatly clarifies the meaning of (what I understand by) aliases.
I think the difference in what we mean is that I intend class aliases to be a true bijection in all contexts (isomorphism?) between a single alias and a set of classes. This is opposed to superclasses which are a one directional implication. One of my main motivations is being able to mix unchanged H98 and H' code (with different numerical hierarchies, and both calling each other) without modifications or prefered treatment for either. this means instances for H' must silently and transparently create instances for H98 classes and vice versa, moreso, type signatures should be compatible. As in, the H' specification should be able to make absolutely no reference to H98 and vice versa, yet class aliases allow one to write a compiler that seamlessly allows mixing code from the two without compromising the design of either. John -- John Meacham - ⑆repetae.net⑆john⑈

| > Crumbs! I have no idea what that means! Did you really mean to repeat "Foo"? According to your | expansion in type signatures | > f :: (Foo a) => ... | > expands to | > f :: (Foo a, Bar a) => ... | > which presumably expands again. I'm totally lost here | | Yes I did, because I wanted to make the differences between class alias | contexts and superclasses very clear, the above context is valid, if | vacuous. the expansion goes as follows . | 1. Foo a --> reduce(Foo a,Bar a) | -- Foo a expanded ... Even more crumbs! Is this fixpoint iteration (being careful to avoid infinite expansion) *really* essential to your proposal? That would be a significant and unwelcome thing IMHO. To be concrete, consider f :: (Foo a) => ... In GHC, f really takes an extra dictionary argument for the class Foo. If aliases mean aliases in the sense of type synonyms (which I think you intend) you must expand Foo to find out whether f takes zero, one, or many dictionary arguments. Furthermore, everyone must expand in precisely the same way, so that we agree on the order of these arguments. That's reasonably simple if "expand" simply means "normalise"; but it's more complicated if there's a fixpoint algorithm involved. So is this really crucial? Simon

On Fri, May 02, 2008 at 11:54:13AM +0100, Simon Peyton-Jones wrote:
Even more crumbs! Is this fixpoint iteration (being careful to avoid infinite expansion) *really* essential to your proposal? That would be a significant and unwelcome thing IMHO.
To be concrete, consider f :: (Foo a) => ...
In GHC, f really takes an extra dictionary argument for the class Foo. If aliases mean aliases in the sense of type synonyms (which I think you intend) you must expand Foo to find out whether f takes zero, one, or many dictionary arguments. Furthermore, everyone must expand in precisely the same way, so that we agree on the order of these arguments. That's reasonably simple if "expand" simply means "normalise"; but it's more complicated if there's a fixpoint algorithm involved.
Yeah, I do mean normalize I think. There is no run time representation of class aliases at all so this issue doesn't arise. f will just take a 'Bar' dictionary argument. I am envisioning class alias expansion taking place very early in the desugaring, certainly before any transformation to ghc core and turning contexts into dictionary arguments.
So is this really crucial?
Probably not, minimal fixpoint calculations are just what I find the simplest way to formally define/think about things. In this case, I am sure a simpler straight up normalization algorithm can be used to get equivalent results... but minimal fixpoints are so easy to implement in haskell and formally well defined that I am not sure of the value of specifying the extension in terms of it. It of course doesn't mean compilers have to perform the fixpoint iteration, it is just a declarative statement of what class aliases are equivalent to. John -- John Meacham - ⑆repetae.net⑆john⑈

hmm, i thought i understood what class aliases were about, but the recent discussion about superclasses and fixpoints has me confused again. may i suggest that the updated proposal page follows this outline (in particular, providing 1 and 2 before diving into 3): 1. general idea + one simple example to confirm intuition (if there are obvious misinterpretations, it might be useful to list and discard them here) 2. translation scheme (formal enough to run all examples) 3. concrete examples to highlight difficult issues and special cases 4. syntactic sugar to make typical uses easier the way i thought i understood them (and the way i encoded them in the example i sent), class aliases consist of two parts: A aliasing of constraints/classes (this is the semantic part that could also be explained by reduction, or by simple mutual implication encodings) B aliasing of syntax, especially instance definitions (this syntactic part is hard to encode, and simple in terms of syntactic macro expansion) so, in writing class alias A x = (B x,C x) we'd get from A: the semantic equivalence of the constraints, which we usually encode roughly like this class (B x,C x) => A x instance (B x,C x) => A x from B: the syntactic equivalence of different ways of defining instances of A,B, and C, which is best understood by expanding all instance definitions for A into instance definitions of B and C defining instances of B x and C x is equivalent to defining an instance of A x (so defining an instance of A x if either B x or C x already exist leads to duplicate instances) everything else is syntactic sugar, such as having methods or constraints in A that are not present in B or C. if this is wrong somewhere, could you please correct it? otherwise, i'll wait for the updated proposal page to explain the details. claus

A aliasing of constraints/classes (this is the semantic part that could also be explained by reduction, or by simple mutual implication encodings)
B aliasing of syntax, especially instance definitions (this syntactic part is hard to encode, and simple in terms of syntactic macro expansion)
it just occurred to me that there is a precedence for this kind of translation, in associated types and type functions. defining an AT in a class is equivalent to defining a TF outside the class, and connecting the TF to the class with superclass and instance constraints, right? class C a where type CT a c :: (a,CT a) instance C a where type CT a = .. c = .. -- vs type family CT a type instance CT a = .. class CT a ~ b => C a where c :: (a,CT a) instance CT a ~ b => C a where c = .. though the latter form is not yet supported in GHC (#714). which leads me to a problem i have with ATs, which applies to class aliases as well: although the ATs are written as if they were local to the class, they get lifted out of the class in a naive manner. in particular, they can only refer to their parameters, not to other local definitions/types, and their parameters have to match the class parameters. however, i assume that the restrictions/translations/implementations for class aliases are similar to the those for the implementation of ATs in terms of TFs, which might help? claus

| which leads me to a problem i have with ATs, which applies | to class aliases as well: although the ATs are written as if they | were local to the class, they get lifted out of the class in a naive | manner. in particular, they can only refer to their parameters, | not to other local definitions/types, and their parameters have | to match the class parameters. I'm not sure what you mean here, Claus. Can you give a concrete example? Simon

| which leads me to a problem i have with ATs, which applies | to class aliases as well: although the ATs are written as if they | were local to the class, they get lifted out of the class in a naive | manner. in particular, they can only refer to their parameters, | not to other local definitions/types, and their parameters have | to match the class parameters.
I'm not sure what you mean here, Claus. Can you give a concrete example?
sure. here's one from practice, even. there was a thread on haskell-cafe on how to re-export FD-based libraries in AT-based form (for better match with AT-based client code). the obvious translation of class FD a b | a -> b would seem to be class AT a where type AT a but, as it turns out, you can't write instance FD a b => AT a where type AT a = b because the 'b' is not in scope! from an AT-based perspective, it ought to be in scope, because the AT definition is local to the instance, but the AT seems to be implemented as sugar for a non-local TF, for which the local 'b' is not available (i'm not sure why there is no lambda-lifting behind the scenes to add that 'b' parameter, in a hidden form?). the thread, and Manuel's explanation, are here: http://www.haskell.org/pipermail/haskell-cafe/2008-March/041168.html this is likely to be less of a problem for class aliases, because the component class instances share not only the same form of instance head, but also the same context (so if a type is functionally determined by the context in one component, it is so in all components). btw, if type family instances could have contexts that functionally determine extra type parameters, the original poster wouldn't have to duplicate his FDs as TFs, as suggested in that email, but could simply write (i think?-): type instance AT a = FD a b => b claus

class alias Foo a => Foo a = Bar a where ...
Has a defined (if not very useful) meaning with class aliases, but is really odd if you consider 'Foo a' a "superclass". So, I think the following terminology should be used:
Context of --+ alias | The alias -+ +--- The expansion of the alias | | | v v v
class alias (S1 a .. Sn a) => A a = (C1 a ... Cn a) where fd1 = .... .... fdn = .... ^ +---- The defaults of the alias
Should all of the arguments of the Ci be the same type variable `a' as in the alias A a or can they be other types as well, like e.g. C1 [a] or C2 Int? Tom -- Tom Schrijvers Department of Computer Science K.U. Leuven Celestijnenlaan 200A B-3001 Heverlee Belgium tel: +32 16 327544 e-mail: tom.schrijvers@cs.kuleuven.be url: http://www.cs.kuleuven.be/~toms/

On Fri, May 02, 2008 at 10:00:32AM +0100, Simon Peyton-Jones wrote:
John
This is good stuff, but I fear that in 3 months time it'll be buried in our email archives. In contrast, your original web page is alive and well, and we were able to start our discussion based on it
So can I suggest that you transfer your web page to the Haskell' wiki (simply a convenient, editable place to develop it), or to the haskell.org wiki (likewise). And that, as the design gets fleshed out, you try to reflect the current state of play there? I don't want this work to be lost!
Yes. I will try to do that. if anyone else wants to go ahead and do it, that would be fine too.
Ok, on to your email:
=============== Desugaring the class alias decl ================= | there are two different desugaring rules, one for instances, one for the | alias appearing anywhere other than an instance declaration: | |
g :: A a => a -> b | > g = ... | | translates to | | > g :: (S a, C1 a, C2 a) => a -> b | > g = ... | | the triplet of (S a, C1 a, C2 a) is completely equivalent to (A a) in | all ways and all places (other than instance heads)
Notice that this part *is* exactly true of a superclass with no methods
class (S a, C1 a, C2 a) => A a where {}
No, this isn't true. imagine
f :: (S a, C1 a, C2 a) => a -> Int f x = g x
g :: A a => a -> Int g x = ....
If A is a class alias, then this compiles just fine, if A is a concrete class with superclasses, then it doesn't necessarily.
That's not necessarily bad; but it does make it harder to figure out when to user a superclass and when to use a class alias. Does that make sense?
In fact, I suggest the following (**): the class alias
class alias S a => A a = (C1 a, C2 a) where f1 = nd1
desugars to
class (S a, C1 a, C2 a) => A a
The class alias decl *also* affects the desugaring of instances, still to come, but by desugaring the class alias into an ordinary class, you don't have to say *anything* about g :: (S a, C1 a, C2 a) => a -> b vs g :: (A a) => a -> b
But there is a difference, as noted above. And how can you decide whether the expansion:
class S a class S a => A a instance A Int
is supposed to declare an instance for 'S Int' as well as 'A Int' or produce an error? Neither is a good choice universally. which is why I made the distinction explicit in my class alias proposal.
=============== Desugaring instanc decls ================= | now for instance declarations | | > instance A a where | > f2 = bf2 | | expands to | | > instance (S a) => C1 a where | > f1 = nd1 | | > instance (S a) => C2 a where | > f2 = bf2 | > f3 = d3
Do you really mean that? Presumably 'a' is not a type variable here? Furthermore, instance decls typically have a context. Unless I have profoundly misunderstood, I think you mean this:
Yeah, a is likely not a type variable, so it will be of form 'S Foo' for some concrete type 'Foo'. Which is checked at compile time (just as if a method of S were used in a default) and produce an error if such an instance doesn't exist.
instance (Foo a, Bar b) => A (a,b) where f1 = bf1
expands to
instance (Foo a, Bar b) => C1 (a,b) where f1 = nd1
instance (Foo a, Bar b) => C2 (a,b) where f2 = bf2 f2 = d3
Notice the *absence* of an instance for (S (a,b)). It's up to the *user* to ensure that there is such an instance, perhaps, say
instance Foo a => S (a,b) where ...
No, the 'S a' as appended to whatever instance context you provide. so
instance (Foo a, Bar b) => A (a,b) where f1 = bf1
expands to
instance (S (a,b), Foo a, Bar b) => C1 (a,b) where f1 = nd1 instance (S (a,b), Foo a, Bar b) => C2 (a,b) where f2 = bf2 f2 = d3
If 'S (a,b)' is not entailed by the environment in scope then the declaration produces an error.
In this way S is behaving just like any ordinary superclass. If we have
class S a => T a then given an instance instance (Foo a, Bar b) => T (a,b) it's up to the user to ensure that there is an instance for S (a,b).
With the desugaring (**) I proposed above, we'd add one more instance: instance (Foo a, Bar b) => A (a,b)
Yes, but we explicitly did not want to add that instance by using a class alias context rather than putting it in the expansion, for a similar reason we don't create a dummy 'Eq' instance when someone declares something an instance of 'Num' even though Eq is a superclass of Num. The 'class alias context' vs 'class alias expansion' is there to make that distinction clear and unambigous, the expansion is what you declare with an instance, the context is a prerequisite for creating an instance. John -- John Meacham - ⑆repetae.net⑆john⑈

| > Notice that this part *is* exactly true of a superclass with no | > methods | > | > class (S a, C1 a, C2 a) => A a where {} | | No, this isn't true. imagine | | > f :: (S a, C1 a, C2 a) => a -> Int | > f x = g x | | > g :: A a => a -> Int | > g x = .... | | If A is a class alias, then this compiles just fine, if A is a | concrete class with superclasses, then it doesn't necessarily. Excellent point. I'll think about that -- meanwhile can you describe this difference explicitly in your writeup? | > =============== Desugaring instanc decls ================= | > | now for instance declarations | > | | > | > instance A a where | > | > f2 = bf2 | > | | > | expands to | > | | > | > instance (S a) => C1 a where | > | > f1 = nd1 | > | | > | > instance (S a) => C2 a where | > | > f2 = bf2 | > | > f3 = d3 | > | > Do you really mean that? Presumably 'a' is not a type variable here? | > Furthermore, instance decls typically have a context. Unless I have | > profoundly misunderstood, I think you mean this: | | Yeah, a is likely not a type variable, so it will be of form 'S Foo' for | some concrete type 'Foo'. Can you give a more general example, like I did, in which we make an instance for A (a,b), where there is a type constructor (your Foo) but also type variables. Perhpas pairs are confusing; try instance ... => A (Foo a b) That's when the context matters! | No, the 'S a' as appended to whatever instance context you provide. so | | > instance (Foo a, Bar b) => A (a,b) where f1 = bf1 | | expands to | | > instance (S (a,b), Foo a, Bar b) => C1 (a,b) where f1 = nd1 | > instance (S (a,b), Foo a, Bar b) => C2 (a,b) where f2 = bf2 f2 = d3 | | If 'S (a,b)' is not entailed by the environment in scope then the | declaration produces an error. I don't understand why. To be concrete, what goes wrong if you omit the (S (a,b))? | of Num. The 'class alias context' vs 'class alias expansion' is there | to make that distinction clear and unambigous, the expansion is what you | declare with an instance, the context is a prerequisite for creating an | instance. I'm not against making such a distinction -- I'm just trying to understand what the distinction is. OK, this is progress. Perhaps rather than responding in detail the next step is to write the proposal up in the light of where we've got to? S

In all honesty what I want is a solution to the problems clearly outlined in the first two sections of his proposal. People may not be clamouring for this (yet), but that maybe simply because they've gotten used to the de facto Haskell style of giant type classes and never seriously asking themselves if maybe there was a better way to do things. Certainly the issues that 1) Num needs broken up, and 2) Functor and Monad should be related, do come up relatively frequently. John's example for 'Lattice' is really spot-on, that is exactly what 1) a developer for an advanced class wants 2) exactly what a developer would like to provide to users (ie the coarser Lattice and BoundedLattice). In my mind that defines the problem to be solved quite nicely. Which gets us to: Simon Peyton-Jones wrote:
I've just had another look, which threw up quite a few questions in my mind. I wonder what would be a good list to discuss it. Maybe this one is not bad, because it has people interested in Haskell innovation, regardless of whether it's a live Haskell' candidate?
I wrote the above because there really are two very different kinds of questions: requirements and design. If there is first a solid agreement on requirements, then it is much simpler to tweak the design. [Motherhood stuff, sorry, but sometimes a reminder of such things is useful]. So in what class are your questions? Since we are talking requirements: to properly follow Parnas' ideas on good design, we should ask ourselves what are the likely future changes in this area. I know of one extension which will definitely be wanted in the future: renamings. For example, mathematicians frequently switch between additive and multiplicative notations for Groups. So one would want to ``inherit'' from a class but allow for a name change at the same time. In effect, one can already to this, ie class Foo a where foo :: a class Foo a => Bar a where bar :: a bar = foo However the difference is that the equility bar = foo is not known intensionally, ie not available to the compiler as something that can be reasoned with. I do believe that that will be an important future consideration. [I could be wrong about how the status of such equalities in the language definition, but I must admit I would be surprised if it were otherwise]. Note that such 'renamings' are a common operation available in "algebraic specification languages" of various kinds. And type classes really are much closer to (algebraic) specifications than anything else, so I do believe that one can learn what are the 'useful operations' from those languages. Proper design then means that a current solution allows for a solution of the above in the future, and definitely does not make such a solution impossible. The solution does not have to be done (either designed, much less implemented) now. [Teaching SE does seem to infect one's mind, doesn't it?] Jacques

Am Dienstag, 22. April 2008 09:33 schrieb Simon Peyton-Jones:
[…]
One reason I've failed to do so is because I have not come under enough pressure! If I thought there were dozens of people slavering at the chops for class aliases, that would put it more firmly on my radar.
I would really, really like to see class aliases (or something similar) in GHC! For example, I’m currently writing lecture notes for a functional programming lecture. During this process I had to see again how unattractive it is that Functor and Applicative aren’t superclasses of Monad. You have to teach your students some historical background in order to make sense of this fact. Even if Functor would have been made a Monad superclass in Haskell 98, the post-98 introduction of Applicative would have prevented it from being a superclass of Monad. I think, we should have the possibility to refine class hierarchies later.
[…]
Best wishes, Wolfgang

Simon Peyton-Jones wrote:
Is this the most up-to-date description of the proposal? http://repetae.net/recent/out/classalias.html
Has anyone looked at my (confusingly named and horribly written) variant? http://haskell.org/haskellwiki/Superclass_defaults My idea is to split class aliases into two separate things: 1. Superclass defaults: allow a class declaration to contain defaults for methods from superclasses. Allow an instance declaration to be for multiple classes at once, using the most specific defaults: instance (FooBar a, Foo a, Bar a) -- pick the defaults from FooBar, since it is a subclass -- of both Foo and Bar. 2. Class aliases: simply an alias for zero or more classes. class alias FooAndBar a = (Foo a, Bar a) In a context FooAndBar a is simply replaced by (Foo a, Bar a). For instantiation purposes the class alias could override the default methods as if it was a subclass of Foo and Bar. No new methods can be added. Alternatively class aliases could be just macros, like type synonyms. Part 1 applies for instance to the Functor/Applicative/Monad hierarchy, fmap could have a default in terms of (>>=). Part 2 is useful for splitting classes up into smaller bits. Twan

Nevertheless, the committee feels that we cannot have a Haskell' without some way to resolve ambiguities when using multi-parameter type classes, be it Functional Dependencies (FDs) or Type Families (TFs).
agreed!
- "Haskell' alpha" will be a complete language specification, including all the modifications and additions we want to make to the language *except* for FDs or TFs.
that should at least allow for some necessary progress.
- Haskell' will follow afterward, adding either FDs or TFs.
that seems to suggest that Haskell' is going to be *the* standard, so it better be very good, and not leave out anything important, even if it is as recent as TFs. strangely, i never had such great expectations from this process - instead, i expected Haskell' to be *one in a series* of standards, documenting the current state, not any (supposedly) final state. it should have come out long ago, and should be well on its way to become obsolete in a few years (though not immediately, if possible). in line with those expectations, i'd be happy with a Haskell' beta, if it were to *standardise*, but *not prescribe* FDs and TFs and whatever other type system extensions have been in common use for a long time now. no Haskell' beta implementation would be required to implement either FDs or TFs or overlap resolution or closed classes or .. . but if i write {-# LANGUAGE TF #-} and the implementation doesn't complain, it'd better conform to the standard for TFs. currently, that is simply not the case, with LANGUAGE pragmas supported (and interpreted differently) by more than one implementation suggesting a *false* sense of standardisation and portability.
The motivation for this two-stage approach is that we can make progress on all the other parts of the language without being blocked on the type system, we can start work on implementing Haskell' alpha in our compilers, users can start using the new standard, and we can gain some experience with using it in practice.
yet another option would be to standardise improvement as a means of reducing ambiguity, and simplification as a means of rewriting constraints, leaving open specific forms of specifying such improvements or simplifications - a bit like a language-standard "API" for adding features (perhaps there could even be a corresponding standard API for extending standard-conformant implementations) [1]. then there could be an addendum specifying FDs as one particular instance of enabling such improvements, and any implementation supporting {-# LANGUAGE FD #-} would have to conform to that addendum. and anyone wanting to support other forms of improvement (records, subtyping, ..) could program to that implementation extension API..
Thanks for your patience :-) And rest assured that progress is being made!
thanks for your update! (frankly, i had written off haskell prime;-) haskell prime is dead! long live haskell prime! claus [1] M. P. Jones. Simplifying and improving qualified types. In FPCA '95: Conference on Functional Programming Languages and Computer Architecture. ACM Press, 1995. http://web.cecs.pdx.edu/~mpj/pubs/improve.html
participants (10)
-
Ben Moseley
-
Claus Reinke
-
Jacques Carette
-
John Meacham
-
Martin Sulzmann
-
Simon Marlow
-
Simon Peyton-Jones
-
Tom Schrijvers
-
Twan van Laarhoven
-
Wolfgang Jeltsch