Re: instance export decls
On Thu, May 01, 2008 at 03:21:11PM +0100, Simon Peyton-Jones wrote: > Indeed! I think it'd be good to allow type signatures, including > instance signatures, in export lists The problem with instance signatures is that it would give the impression that it would be possible to _not_ export an instance, and it wouldn't make explicit the fact that the instances of all modules it depends on are also exported. John -- John Meacham - ⑆repetae.net⑆john⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Haskell' - class aliases
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 ||| vvv 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: [EMAIL PROTECTED] url: http://www.cs.kuleuven.be/~toms/ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Haskell' - class aliases
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 ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Haskell' - class aliases
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 ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Haskell' - class aliases
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⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
RE: Haskell' - class aliases
| > 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 ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Haskell' - class aliases
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⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
RE: Haskell' - class aliases
| > 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 ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
RE: Haskell' - class aliases
| 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 ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Haskell' - class aliases
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⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mai
Re: Haskell' - class aliases
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 ||| vvv 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 ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Haskell' - class aliases
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 ||| vvv > 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⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
Re: Haskell' - class aliases
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⑈ ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime
RE: Haskell' - class aliases
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 ___ Haskell-prime mailing list Haskell-prime@haskell.org http://www.haskell.org/mailman/listinfo/haskell-prime