| ... | ... | @@ -9,27 +9,14 @@ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For removal:
|
|
|
|
Problems with the current defaulting rule:
|
|
|
|
|
|
|
|
|
|
|
|
- Defaults are limited to certain classes. A tool like Hat, which transforms Haskell source, cannot transform the defaults, because there is no way make defaults apply to the transformed classes rather than the original ones.
|
|
|
|
- Defaults are limited to Prelude numeric classes. A tool like Hat, which transforms Haskell source, cannot transform the defaults, because there is no way make defaults apply to the transformed classes rather than the original ones.
|
|
|
|
|
|
|
|
- Defaults cannot be applied to user-defined classes. It would be nice for some applications to be able to allow a default clause to name the class being defaulted over, as well as the type to choose. Examples include QuickCheck?, where you might wish to default the Arbitrary class to something small (e.g. Bool) when a value is otherwise unconstrained.
|
|
|
|
|
|
|
|
For fixing:
|
|
|
|
|
|
|
|
|
|
|
|
- Report specification when it comes to defaulting is impossible to implement when general recursive modules are allowed.
|
|
|
|
- It should be specified that a group of mutually recursive modules must have exactly the same defaulting.
|
|
|
|
|
|
|
|
|
|
|
|
For replacement:
|
|
|
|
|
|
|
|
|
|
|
|
- Perhaps require a default clause to name the class being defaulted over, as well as the type to choose.
|
|
|
|
|
|
|
|
|
|
|
|
Other issues:
|
|
|
|
|
|
|
|
- Report specification of defaulting is impossible to implement in the presence of recursive modules. (Should it be specified that a group of mutually recursive modules must have exactly the same defaulting?)
|
|
|
|
|
|
|
|
- A default clause applies only within the module containing the declaration. Defaults can be neither
|
|
|
|
exported nor imported. Does anyone wish to propose import/export of defaults?
|
| ... | ... | @@ -38,7 +25,7 @@ Other issues: |
|
|
|
- Against import/export: a change in the imports of a module might silently change behavior
|
|
|
|
- A compromise might be to allow defaults which will be inherited to be specified only in the module that defines a class, but groups of mutually recursive modules may override defaulting locally. this will avoid the import changing behavior problem and allow some sort of inheritence of defaults.
|
|
|
|
|
|
|
|
## proposal 1
|
|
|
|
## Proposal 1
|
|
|
|
|
|
|
|
|
|
|
|
|
| ... | ... | @@ -59,53 +46,122 @@ Classes without defaults will have the equivalent of an empty list of types, so |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
It is important to specify "unambiguous", because in the case of
|
|
|
|
One problem of course is what does "unambiguous" mean? In the case of
|
|
|
|
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
default A (Int, String, ())
|
|
|
|
default B (String, Int, ())
|
|
|
|
(A t, B t) => t
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
the only valid default for a type in both classes A and B should be (). This avoids making an arbitrary choice based on
|
|
|
|
textual ordering of the default declaration clauses.
|
|
|
|
any of the three types would satisfy the rule that it is an instance of all the classes. So what is the intepretation of "first" in the list?
|
|
|
|
The only unambiguous interpretation of "first" valid type in both classes A and B would be (), to avoid making an arbitrary choice between Int and String.
|
|
|
|
However, this is still far from clearcut. Consider the following examples:
|
|
|
|
|
|
|
|
|
|
|
|
### pro
|
|
|
|
```wiki
|
|
|
|
default A (Int,String,())
|
|
|
|
default B (String,(),Int)
|
|
|
|
(A t, B t) => t
|
|
|
|
|
|
|
|
default C (Int, Double, String, ())
|
|
|
|
default D (Double,String,Int,())
|
|
|
|
(C t, D t) => t
|
|
|
|
```
|
|
|
|
|
|
|
|
### Pro
|
|
|
|
|
|
|
|
|
|
|
|
- very useful in interactive interpreter
|
|
|
|
- less ad hoc than current method
|
|
|
|
- overcomes the Hat transformation problem
|
|
|
|
|
|
|
|
### con
|
|
|
|
### Con
|
|
|
|
|
|
|
|
|
|
|
|
- can not exactly replicate behavior of existing defaulting mechanism, but can come close.
|
|
|
|
- might hide errors, an optional warning on defaulting should be possible.
|
|
|
|
- not clear how to choose a single unambiguous member of more than one list of types
|
|
|
|
|
|
|
|
## Proposal 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Change the syntax of the defaulting clause from:
|
|
|
|
Change default decls to (a) name the class being defaulted, and (b) permit only one default type per class, rather than a list.
|
|
|
|
Possible syntax, by analogy with instance decls:
|
|
|
|
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
topdecl -> default tycls type
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Semantically, which type is chosen is determined by the class context at the choice point (after context simplification). That is, in
|
|
|
|
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
default C T
|
|
|
|
(C a) => a
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
the type 'a', constrained by class 'C' but otherwise unresolved, is instantiated to the sole type 'T'.
|
|
|
|
If there is no unique choice, for instance because more than one class-with-a-default is mentioned in the context (and their default types are different), then it is a static error. Note that it is OK to have several classes in the context of the defaultable value, provided only one of them is declared to yield a default type, or if more
|
|
|
|
than one yields a type, those types are the same. Choosing a default after context simplification means that no conflicts between super- and sub-classes can arise.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Some examples:
|
|
|
|
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
default Eq Integer
|
|
|
|
default Fractional Float
|
|
|
|
(Eq a, Fractional a) => a
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
After simplification, this becomes (Fractional a)⇒a, because Eq is a superclass of Fractional. Thus, there is a single class to be defaulted, and Float is chosen.
|
|
|
|
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
default Ord Integer
|
|
|
|
default Fractional Float
|
|
|
|
(Ord a, Fractional a) => a
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
A static error, because Ord is not a superclass of Fractional, so the context does not simplify, and the default types disagree.
|
|
|
|
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
default Bounded Int
|
|
|
|
default Ord Int
|
|
|
|
(Ord a, Bounded a) => a
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Default is Int, because even though the context does not simplify, the classes involved do agree on which default to choose.
|
|
|
|
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
topdecl -> default ( type_1 , ... , type_n )
|
|
|
|
default Bounded Int
|
|
|
|
default Ord Int
|
|
|
|
(Ord a, Bounded a, Show a) => a
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
to
|
|
|
|
Default is Int, same as above, except for the extra constraint Show a. There is no default declared for Show, so the remaining context is used to make the choice.
|
|
|
|
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
topdecl -> default ( tycls_1 => type_1 , ... , tycls_n => type_n )
|
|
|
|
(Show a, Read a) => a
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Semantically, which type is chosen is determined by the class context at the choice point (after context simplification). If there is no unique choice, for instance because more than one class-with-a-default is mentioned in the context (and their default types are different), then it is a static error. Note that it is OK to have several classes in the context of the defaultable value, provided only one of them is declared to yield a default type, or if more than one yields a type, those types are the same. Choosing a default after context simplification means that no conflicts between super- and sub-classes can arise.
|
|
|
|
A static error, because there are no defaults declared for any of the classes involved.
|
|
|
|
|
|
|
|
|
|
|
|
|
| ... | ... | |