resolving missing class instances @ compile time

Here's a little quirk I ran into recently. While making a little vector data type in class Num (code below), I didn't implement an instance of "fromInteger" (thinking I didn't need it). Well as you can probably guess, it turns out I did need it, and subsequently got a run time exception. Which surprised me a little, since it seems like it could have been caught at compile time. (Although I did ignore a warning). This has probably been answered a thousand times, but could someone point me in the direction of documentation explaining why it compiled? Thanks, Greg Buchholz --uncomment out "fromInteger" to get a working program instance Num Vec where (V a b c) + (V x y z) = (V (a+x) (b+y) (c+z)) (V a b c) - (V x y z) = (V (a-x) (b-y) (c-z)) --fromInteger 0 = V 0.0 0.0 0.0 instance Eq Vec where (V a b c) == (V x y z) = (a==x) && (b==y) && (c==z) data Vec = V !Double !Double !Double deriving Show main = print $ sum [(V 1.0 2.0 3.0), (V 4.0 5.0 6.0)]

On Wed, 2005-05-04 at 17:18 -0700, Greg Buchholz wrote:
Here's a little quirk I ran into recently. While making a little vector data type in class Num (code below), I didn't implement an instance of "fromInteger" (thinking I didn't need it). Well as you can probably guess, it turns out I did need it, and subsequently got a run time exception. Which surprised me a little, since it seems like it could have been caught at compile time. (Although I did ignore a warning). This has probably been answered a thousand times, but could someone point me in the direction of documentation explaining why it compiled?
Thanks,
Greg Buchholz
Hi Greg, Perhaps this section of the report might help:
From Section "4.3.2 Instance Declarations" in the Haskell Report:
http://www.haskell.org/onlinereport/decls.html#instance-decls "If no binding is given for some class method then the corresponding default class method in the class declaration is used (if present); if such a default does not exist then the class method of this instance is bound to undefined and no compile-time error results." Cheers, Bernie.

Bernard Pope wrote:
Perhaps this section of the report might help:
From Section "4.3.2 Instance Declarations" in the Haskell Report:
http://www.haskell.org/onlinereport/decls.html#instance-decls
"If no binding is given for some class method then the corresponding default class method in the class declaration is used (if present); if such a default does not exist then the class method of this instance is bound to undefined and no compile-time error results."
I may be missing the big picture, but why is this behavior more desirable than failing with an error at compile time? Greg Buchholz

On 12/05/05, Greg Buchholz
Bernard Pope wrote:
Perhaps this section of the report might help:
From Section "4.3.2 Instance Declarations" in the Haskell Report:
http://www.haskell.org/onlinereport/decls.html#instance-decls
"If no binding is given for some class method then the corresponding default class method in the class declaration is used (if present); if such a default does not exist then the class method of this instance is bound to undefined and no compile-time error results."
I may be missing the big picture, but why is this behavior more desirable than failing with an error at compile time?
Aren't the warnings just about as usefull as failures? Anyway, you could always use the -Werrror flag for ghc... In any case, I would not like to have to implement an entire typeclass at once... it would interfere with incremental development. -- Sam

Samuel Bronson wrote:
Aren't the warnings just about as usefull as failures? Anyway, you could always use the -Werrror flag for ghc...
In any case, I would not like to have to implement an entire typeclass at once... it would interfere with incremental development.
Hmm. I guess I'm doing a terrible job of asking my question. I don't want to implement the entire typeclass either. Just the part that my program actually uses. Why can't the fact that my program uses an unimplemented instance of a class be statically determined? Is there a theoretical reason it can't be done? Is it more convienient for compiler/specification writers this way? Is it just because that's the way its always been done? Curious, Greg Buchholz

On 12/05/05, Greg Buchholz
Samuel Bronson wrote:
Aren't the warnings just about as usefull as failures? Anyway, you could always use the -Werrror flag for ghc...
In any case, I would not like to have to implement an entire typeclass at once... it would interfere with incremental development.
Hmm. I guess I'm doing a terrible job of asking my question. I don't want to implement the entire typeclass either. Just the part that my program actually uses. Why can't the fact that my program uses an unimplemented instance of a class be statically determined? Is there a theoretical reason it can't be done? Is it more convienient for compiler/specification writers this way? Is it just because that's the way its always been done?
After thinking about it for a while, I'm positive it would be a LOT of work to get that to work in general, if it is even possible. Even getting it to work in only specific, limited cases (such as within a module) would probably not be easy, since it is such an indirect kind of thing. It probably wouldn't be all that usefull anyway, either. In general, though, I think they don't implement stuff like this unless someone specifically wants to *use* it. -- Sam

Samuel Bronson wrote:
After thinking about it for a while, I'm positive it would be a LOT of work to get that to work in general, if it is even possible. Even getting it to work in only specific, limited cases (such as within a module) would probably not be easy, since it is such an indirect kind of thing. It probably wouldn't be all that usefull anyway, either.
(This is my last time, I promise). Why? Here's my thought process. Let's say I a have a program like... main = print $ (foo 42) ...(that's the whole thing). The compiler parses it, determines that "foo" is a function being applied to "42" and tries to look up "foo" in the symbol table. That fails because there is no function "foo". Why is it any different if foo is part of some type class? We must know where to look for "foo" since we know the type of "foo" from its arguments and return value (it passed the type checker after all). Confused, Greg Buchholz

On 12/05/05, Greg Buchholz
Samuel Bronson wrote:
After thinking about it for a while, I'm positive it would be a LOT of work to get that to work in general, if it is even possible. Even getting it to work in only specific, limited cases (such as within a module) would probably not be easy, since it is such an indirect kind of thing. It probably wouldn't be all that usefull anyway, either.
(This is my last time, I promise). Why? Here's my thought process. Let's say I a have a program like...
main = print $ (foo 42)
...(that's the whole thing). The compiler parses it, determines that "foo" is a function being applied to "42" and tries to look up "foo" in the symbol table. That fails because there is no function "foo". Why is it any different if foo is part of some type class? We must know where to look for "foo" since we know the type of "foo" from its arguments and return value (it passed the type checker after all).
Well, it would have to know that foo was being applied to 42 at whatever type it was being applied at (Might be Int), and it would also have to know that the instance declaration for foo's typeclass at that type did bind foo. Now this might not sound so bad, but consider the case where you call a function bar :: Foo a => a -> String, which is not part of the typeclass but is defined in another module. Then GHC would have to know which methods of Foo were implemented at whatever type the value you applied bar to had, and which methods bar actually called. The former may not be hard, but the latter would require functions with typeclass constraints on their types to be annotated in the interface file with what typeclass methods they called. Does that sound hard yet?

Samuel Bronson wrote:
The former may not be hard, but the latter would require functions with typeclass constraints on their types to be annotated in the interface file with what typeclass methods they called. Does that sound hard yet?
Compared to writing the rest of the compiler? No. I've never written a Haskell compiler, but I'd say it sounds like it might be a tedious book-keeping problem though. Greg Buchholz

On 12/05/05, Greg Buchholz
Samuel Bronson wrote:
The former may not be hard, but the latter would require functions with typeclass constraints on their types to be annotated in the interface file with what typeclass methods they called. Does that sound hard yet?
Compared to writing the rest of the compiler? No. I've never written a Haskell compiler, but I'd say it sounds like it might be a tedious book-keeping problem though.
Most of the rest of the compiler is already written, though ;-)

Greg Buchholz wrote:
Samuel Bronson wrote:
The former may not be hard, but the latter would require functions with typeclass constraints on their types to be annotated in the interface file with what typeclass methods they called. Does that sound hard yet?
Compared to writing the rest of the compiler? No. I've never written a Haskell compiler, but I'd say it sounds like it might be a tedious book-keeping problem though.
It's quite possible, but also very tedious. -- Lennart

Hello Greg, Friday, May 13, 2005, 12:47:54 AM, you wrote: GB> Samuel Bronson wrote:
After thinking about it for a while, I'm positive it would be a LOT of work to get that to work in general, if it is even possible. Even getting it to work in only specific, limited cases (such as within a module) would probably not be easy, since it is such an indirect kind of thing. It probably wouldn't be all that usefull anyway, either.
GB> (This is my last time, I promise). Why? Here's my thought process. GB> Let's say I a have a program like... GB> main = print $ (foo 42) GB> ...(that's the whole thing). The compiler parses it, determines that GB> "foo" is a function being applied to "42" and tries to look up "foo" in GB> the symbol table. That fails because there is no function "foo". Why GB> is it any different if foo is part of some type class? We must know GB> where to look for "foo" since we know the type of "foo" from its GB> arguments and return value (it passed the type checker after all). because compiler can't determine logic of your program. and your program may use some class methods only for some of types for which this class is defined -- Best regards, Bulat mailto:bulatz@HotPOP.com
participants (6)
-
Bernard Pope
-
Bulat Ziganshin
-
Greg Buchholz
-
Greg Buchholz
-
Lennart Augustsson
-
Samuel Bronson