[Haskell-cafe] Extending the idea of a general Num to other types?
Sterling Clover
s.clover at gmail.com
Wed Sep 5 13:43:14 EDT 2007
I'd prefer something slightly more specific, such as instead of
No instance for (Num String)
arising from use of `+' at test/error.hs:2:8-17
Possible fix: add an instance declaration for (Num String)
In the expression: x + (show s)
In the definition of `f': f x s = x + (show s)
Maybe
(+) is defined as Num a => a -> a -> a
The second argument of (+) is of type String at test/
error.hs:2:8-17
String is not an instance of Num
In the expression: x + (show s)
In the definition of `f': f x s = x + (show s)
It seems to me that anybody who gets declaring classes and instance
declarations already will be able to figure out pretty quickly what
to do if they wanted, e.g., Complex to be an instance of Real with a
message like that. The problem is there's too much special-casing
otherwise, since its not just Strings but other standard numeric
types. For example, if I take "mod $ sqrt n $ m" then I probably
don't want to declare an instance of "Floating Int" but just want to
use a conversion operator like ceiling. Here's another related thing
I ran into below (example simplified):
testErr :: Integral a => a -> [a]
testErr n =
ceiling $ (exp $ sqrt $ log n) ** (1/2)
testMe.hs:8:14:
Could not deduce (Floating a) from the context (Integral a)
arising from use of `**' at testMe.hs:8:14-42
Possible fix:
add (Floating a) to the type signature(s) for `testErr'
In the second argument of `($)', namely
`(exp $ (sqrt $ (log n))) ** (1 / 2)'
In the expression: ceiling $ ((exp $ (sqrt $ (log n))) ** (1 / 2))
In the definition of `testErr':
testErr n = ceiling $ ((exp $ (sqrt $ (log n))) ** (1 / 2))
What I needed to do here was cast n using realToFrac (or at least I
did that and it seemed to be the right decision). But, again, the
compiler is suggesting that I declare something that's already an
Integral as a Floating, which is conceptually similar to declaring an
instance of "Floating Integral" (after all, it implies that such an
instance can be/has been declared). Here the possible fix is a great
deal more likely to be right, however, so I'm not sure it should be
changed, except that a beginner might go and change Integral to
Floating when they really *wanted* an Integral for other reasons. The
real problem seems to be that the top level expression it returns is
pretty huge.
If I remove the " ** (1/2)" then I get a message closer to the one
I'd like:
Could not deduce (Floating a) from the context (Integral a)
arising from use of `log' at testMe.hs:8:28-32
Possible fix:
add (Floating a) to the type signature(s) for `testErr'
In the second argument of `($)', namely `log n'
In the second argument of `($)', namely `sqrt $ (log n)'
In the second argument of `($)', namely `(exp $ (sqrt $ (log n)))'
This seems like something more complicated with how the type-
inference system works, and may not be as easily soluble, however.
Alternately, it might lead to huge error-stack blowups in more
complicated expressions?
Again, relatedly, and now I'm *really* digressing, if I don't fix a
type signature for testErr but write it so that it needs conflicting
types of n then I get (calling the function from main):
testErr n =
mod n $ ceiling $ (exp $ sqrt $ log n)
Ambiguous type variable `a' in the constraints:
`Integral a' arising from use of `testErr' at testMe.hs:20:26-35
`RealFrac a' arising from use of `testErr' at testMe.hs:20:26-35
`Floating a' arising from use of `testErr' at testMe.hs:20:26-35
Probable fix: add a type signature that fixes these type variable
(s)
Again, its probably too much to ask of the type-inference system that
it catch this type error in parsing testErr itself. And the error
message is pretty helpful because if I set a type signature, then it
forces me to figure out the conflict. Still, if it could expand with
which elements of testErr caused it to infer each type (if there is
no explicit signature, there is), then maybe that could be useful?
--S
On Sep 5, 2007, at 4:56 AM, Simon Peyton-Jones wrote:
>
> Is your suggestion specific to String? E.g. if I wrote
>
> data Complex = MkC Float Float
>
> real :: Float -> Complex
> real f = MkC f 0
>
> f x s = x + real 1
>
> then I really might have intended to use Complex as a Num type, and
> the suggestion is precisely on target. I'd be interested to know
> this particular "helpful suggestion" on GHC's part is more
> misleading than useful. What do others think?
>
> | rephrase to something like "String is not an instance of Num"?
> For a
> | newbie, it may not be clear that Num is the class and String is the
> | type.
>
> Good point. Not so easy for multi-parameter type classes! E.g. No
> instance for (Bar String Int). So we could have
>
> String is not an instance of class Foo -- single param
> No instance for (Bar String Int) -- multi-param
>
> Would that be better (single-param case is easier), or worse
> (inconsistent)?
>
> Simon
More information about the Haskell-Cafe
mailing list