Dear Sverker Nilsson,
Thanks for your message - interesting ideas and interesting questions.
[I'm copying the reply to the Haskell mailing list in case anyone
wishes to support your suggestions.]
First, one of Haskell's annoying features is that the scope of a type
variable in a type signature or instance heading only extends over the
signature. So, when you want to write:
> instance (FromInt a, ToInt a, MinVal a, MaxVal a) => Enum a where
> enumFrom c = map fromInt [toInt c .. toInt (maxVal :: a)]
It doesn't work (because the "a" isn't in scope during the
declarations) - you have to use "asTypeOf" instead:
> instance (FromInt a, ToInt a, MinVal a, MaxVal a) => Enum a where
> enumFrom c = map fromInt [toInt c .. toInt (maxVal `asTypeOf` c)]
While developing something like the proposed "Bounded" class,
you introduced separate classes for minVal and maxVal observing:
> Something having a minimum value, in my view, didn't necessarily
> imply it would have a maximum value.
Yes, perfectly true. The best example is that there's a minimal list
(the empty list) but even though there's a maximal Char (say), there's
no maximal list of characters.
Our primary motivation for adding Bounded is to clean up the
{min,max}{Char,Int} situation and make the derived Enum instances
slightly more regular (similar in spirit to your definitions above).
For this purpose, insisting on having both a min and a max isn't a
problem.
However, for other purposes, having one bound but not the other is
certainly possible and maybe useful.
(I agree that defining a bogus instance in which "minVal" (say) is
defined but "maxVal" is undefined or has a bogus value is at least
untidy and at worst a bug waiting to happen. I tried (and failed) to
get the Text instance of (a -> b) removed from the Prelude for this
reason.)
The major disadvantage of separating the two is that it introduces
even more classes.
If you read the preludechanges document carefully, you'll see that
(even at this late stage) these are only proposed changes. Glasgow
argue that it's hard enough to keep Ix and Enum separate in your mind
- adding another can only worsen things.
You were then surprised and disturbed to find that this isn't legal
Haskell:
> class (MinVal a, MaxVal a)=>Bounded a
>
> instance Bounded T where
> maxVal = T3
> minVal = T1
There was a proposal to make this legal. As far as I know, there's no
technical problems here - I guess it just got forgotten about (or the
proposer decided that Haskell 1.3 had too many changes in it already!)
> * Should Bounded be derived from Ord?
>
> The Bounded class that was suggested for Haskell 1.3 was derived from
> Ord. Myself playing with similar things I derived MinVal and MaxVal
> from nothing - I thought this more general. Maybe the reason for
> having Bounded derived from Ord was to imply that its functions shall
> satisfy certain laws, probably as being min/max as defined by the
> ordering functions in Ord. But as I don't see how this can be
> guaranteed by deriving Bounded from Ord, I would think that it could
> as well be standalone (or derived from something like MinBound and
> MaxBound if possible); for more generality and less dependency between
> the classes in the system.
Yes, the sole reason is because it seemed tidier to specify Ord -
without knowing which comparision is being used, it doesn't make much
sense to say you have a "maximum value".
> For example, the new proposal says:
>
> > ...
> > Programmers are free to define a class for partial orderings; here, we
> > simply state that Ord is reserved for total orderings.
>
> That seems to imply also that a programmer should not use Bounded on
> types that have no total ordering. I believe this might be an unnecessary
> restriction.
It certainly looks that way.
> > The names fromEnum and toEnum are misleading since
> > their types involve both Enum and Bounded. We couldn't face writing
> > fromBoundedEnum and toBoundedEnum. Suggestions
> > welcome.
>
> Maybe names like ToInt and FromInt could be used for this?
>
> How about the following, assuming the proposed diff and succ functions:
>
> class (Bounded a, Enum a) => ToInt a where toInt :: a -> Int [...]
> class (Bounded a, Enum a) => FromInt a where fromInt :: Int -> a [...]
These names look good. Three _minor_ concerns:
1) It introduces even more standard classes to confuse programmers
with. Why allow the programmer to override them?
2) Several implementations have added a non-standard method
fromInt :: Int -> a
to the Num class to avoid unnecessary uses of fromInteger.
However, I think most normal uses would work unchanged if "fromInt"
had type:
fromInt :: (Bounded a, Enum a) => Int -> a
3) There is a weak tradition of putting the name of the class into the
name of the method.
This tradition is often broken when it would get in the way of a
good name.
Action:
1) I'll remove the Ord context from Bounded.
2) I'll use fromInt/toInt names instead of toEnum/fromEnum
3) I'll add comments about separating Bounded into two parts to
the document.
Alastair Reid
Yale Haskell Project