Type classes are not like interfaces, after all

Hi all. It's about a month I'm trying to learn haskell in my spare time ( and, I should add, with my spare neuron :-). I made progress, but more slowly than I expected :-(. I started hasking question on comp.lang.haskell (I like newsgroups more than mailing lists), but then realized that this may be a better places for my newbie questions. So, here comes the first ... As many beginners coming from OOP, i made the mental equation 'type class == interface'. It worked well ... unitil yesterday. Then I discovered that this piece of code (1) is illegal in askell (ghc gives the 'rigid type variable' error) Num n => a :: n a = 3 :: Integer I also discovered that this (2) instead is legal: Num n => a :: n a = 3 because it is implicitely translated into (3): Num n => a :: n a = fromInteger 3 But I don't understand the difference between (1) and (3). I could not find the implementation of fromInteger, but I suspect that it goes like this: instance Num Integer where fromInteger x -> x To me, it looks like (1) and (3) are equal, but that the compiler needs a sort of 'formal reassurance' that the value yielded by 'a' is of Num type. But since Integer is an instance of Num, I expected that (1) was already giving this reassurance. At least, it works this way with interfaces in Java and with class pointers in C++. But obviously there is something I don't get here ... Anybody here can explain? Ciao ----- FB

Hello, It seems like you have some trouble with grasping the concept of polymorphism in this particular case. The function reverse has the following type: reverse :: [a] -> [a] This means that this function will work on a list containing any type, and it will return that same type. A sort function would have a type like this: sort :: (Ord a) => [a] -> [a] This function also doesn't care what type the list is filled with, but does require this type to be an instance of the Ord class (so it can be ordered). Now an example with the Num class: (+) :: (Num a) => a -> a -> a This function works on any type which is an instance of Num, for example Int and Integer. The type of fromInteger is: fromInteger :: (Num a) => Integer -> a You are correct that literal numbers are internally rewritten, so 3 becomes fromInteger 3, giving the following type: fromInteger 3 :: (Num a) => a This means that fromInteger 3 is of type a, for any a that is an instance of Num. In your example (which seems to be in a bad syntax), you're saying that a must be of type: a :: (Num n) => n So this is the same type as fromInteger 3. So just saying that a = 3 or a = fromInteger 3 will work here. The function a is polymorphic, just like sort or (+) are. They (have to) work for any type that implements the corresponding type class. The problem comes from the extra type annotation: a = 3 :: Integer Which says that instead of being of any numeric type, a is of type Integer. This is less general, since you can't use it when you need an Int or a Double for example. I hope I explained it clearly. If not please reply. Paul Francesco Bochicchio wrote:
Hi all. It's about a month I'm trying to learn haskell in my spare time ( and, I should add, with my spare neuron :-). I made progress, but more slowly than I expected :-(. I started hasking question on comp.lang.haskell (I like newsgroups more than mailing lists), but then realized that this may be a better places for my newbie questions. So, here comes the first ...
As many beginners coming from OOP, i made the mental equation 'type class == interface'. It worked well ... unitil yesterday.
Then I discovered that this piece of code (1) is illegal in askell (ghc gives the 'rigid type variable' error)
Num n => a :: n a = 3 :: Integer
I also discovered that this (2) instead is legal:
Num n => a :: n a = 3
because it is implicitely translated into (3):
Num n => a :: n a = fromInteger 3
But I don't understand the difference between (1) and (3). I could not find the implementation of fromInteger, but I suspect that it goes like this:
instance Num Integer where fromInteger x -> x
To me, it looks like (1) and (3) are equal, but that the compiler needs a sort of 'formal reassurance' that the value yielded by 'a' is of Num type. But since Integer is an instance of Num, I expected that (1) was already giving this reassurance. At least, it works this way with interfaces in Java and with class pointers in C++.
But obviously there is something I don't get here ...
Anybody here can explain?
Ciao ----- FB
------------------------------------------------------------------------
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

2009/1/23 Paul Visschers
Hello,
It seems like you have some trouble with grasping the concept of polymorphism in this particular case.
<...>
I think I get the polymorphism. What I don't get is why a specialized type cannot replace a more generic type, since the specialized type implements the interface defined in the generic type. As I tried to explain, I'm probably misled by the comparison with OOP languages, where polymorphism is implemented via interfaces (Java) or abstract classes/methods (C++). For instance in Java you can write: AbstractInterface a = new ConcreteClass(); if ConcreteClass implements AbstractInterface. The complier will handle a as an instance of AbstractInterface, ignoring anything that is not specifed in its declaration. This make the substitution safe: for instance calling a.AbstractInterfaceMethod() is fine, while calling a.ConcreteClassMethod() gives you an error. Now, I understand that this is not the case in haskell, and I understand the formal reason you give for this. What I am trying to understand is the _actual_ reason behind the formal reason: usually a programming language place limits to avoid ambiguity or misuse of language feature. <...> The problem comes from the extra type annotation: a = 3 :: Integer Which says that instead of being of any numeric type, a is of type Integer. This is less general, since you can't use it when you need an Int or a Double for example. This is what I don't get : why yielding an Integer is not good enough for a function that promises to yield a num? What is missing in Integer?
I hope I explained it clearly. If not please reply.
Paul
You have been clear. I'm probably still too bound to OOP world. Thanks. Ciao ------- FB

On 23 Jan 2009, at 14:37, Francesco Bochicchio wrote:
2009/1/23 Paul Visschers
Hello, It seems like you have some trouble with grasping the concept of polymorphism in this particular case.
<...>
I think I get the polymorphism. What I don't get is why a specialized type cannot replace a more generic type, since the specialized type implements the interface defined in the generic type.
Suppose I declare this constant: x :: Num a => a x = 3 :: Integer Now suppose I want to use that in a function. It's type signature says that x is *any* numeric type i want it to be, so I'm going to add it to another numeric value: y :: Complex Float y = x + (5.3 :+ 6.93) Unfortunately, x *can't* take the form of any Numeric type – it has to be an Integer, so I can't add it do Complex Floating point numbers. The type system is telling you "while Integers may imelement the numeric interface, the value 3 :: Integer is not a generic value – it can't take the form of *any* numeric value, only a specific type of numeric values". Bob

Francesco Bochicchio
I think I get the polymorphism. What I don't get is why a specialized type cannot replace a more generic type, since the specialized type implements the interface defined in the generic type.
Don't confuse this kind of polymorphism with the one you usually find in OOP languages. Consider your original function again. Let me change its name to make it clearer: arbitraryNum :: Num n => n arbitraryNum = 3 :: Integer If 'arbitraryNum' is referenced somewhere else, then the result is expected to be of type 'Num n => n'. It may be referenced as an Integer, as a Float, as a complex number, etc., whatever is a Num instance is valid here. So the value you're trying to give 'arbitraryNum' is of a too specific type. You're trying to use information, which you don't have, namely that 'n' is an Integer. The only information about 'n' you have is that it's a Num type. Now the Num type class contains the fromInteger function, which you can use to convert any Integer to a Num type. So you can write: arbitraryNum :: Num n => n arbitraryNum = fromInteger (3 :: Integer) You take 3, which is an Integer, and use fromInteger to convert it to 'Num n => n'. Try to think for a moment about what the outcome of the following function can be: arbitraryValue :: a arbitraryValue = ...? You should come to the conclusion that arbitraryValue cannot return anything, because it doesn't have any information about what it's supposed to return. What is the type 'a'? What information do you have about 'a'? No information. Not even enough information to construct a value of type 'a'. The type 'a' is most polymorphic and its value is least defined. Of course, you can write such a function, under the strict requirement that it never returns a value. You say, the result of such a function is 'bottom'. For example: bottom :: a bottom = bottom This function never returns. It recurses forever. Greets, Ertugrul. -- nightmare = unsafePerformIO (getWrongWife >>= sex) http://blog.ertes.de/

AbstractInterface a = new ConcreteClass();
In Java, if a variable has type AbstractInterface, it is *existentially* quantified: it means, this variable references a value of *some* type, and all you know about it is that it is an instance of AbstractInterface. Whatever code sets the value of the variable gets to choose its concrete type; any code that uses the variable cannot choose what type it should be, but can only use AbstractInterface methods on it (since that's all that is known). However, a Haskell variable with type var :: AbstractInterface a => a is *universally* quantified: it means, this variable has a polymorphic value, which can be used as a value of any type which is an instance of AbstractInterface. Here, it is the code which *uses* var that gets to choose its type (and indeed, different choices can be made for different uses); the definition of var cannot choose, and must work for any type which is an instance of AbstractInterface (hence it must only use methods of the AbstractInterface type class). Writing a :: Num n => n a = 3 :: Integer is trying to define a universally quantified value as if it were existentially quantified. Now, it *is* possible to have existentially quantification in Haskell; I can show you how if you like, but I think I'll stop here for now. Does this help? -Brent

I think this explanation is positively brilliant. Byorgey++
On Fri, Jan 23, 2009 at 9:59 AM, Brent Yorgey
AbstractInterface a = new ConcreteClass();
In Java, if a variable has type AbstractInterface, it is *existentially* quantified: it means, this variable references a value of *some* type, and all you know about it is that it is an instance of AbstractInterface. Whatever code sets the value of the variable gets to choose its concrete type; any code that uses the variable cannot choose what type it should be, but can only use AbstractInterface methods on it (since that's all that is known).
However, a Haskell variable with type
var :: AbstractInterface a => a
is *universally* quantified: it means, this variable has a polymorphic value, which can be used as a value of any type which is an instance of AbstractInterface. Here, it is the code which *uses* var that gets to choose its type (and indeed, different choices can be made for different uses); the definition of var cannot choose, and must work for any type which is an instance of AbstractInterface (hence it must only use methods of the AbstractInterface type class).
Writing
a :: Num n => n a = 3 :: Integer
is trying to define a universally quantified value as if it were existentially quantified.
Now, it *is* possible to have existentially quantification in Haskell; I can show you how if you like, but I think I'll stop here for now.
Does this help?
-Brent _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

2009/1/23 Brent Yorgey
AbstractInterface a = new ConcreteClass();
In Java, if a variable has type AbstractInterface, it is *existentially* quantified: it means, this variable references a value of *some* type, and all you know about it is that it is an instance of AbstractInterface. Whatever code sets the value of the variable gets to choose its concrete type; any code that uses the variable cannot choose what type it should be, but can only use AbstractInterface methods on it (since that's all that is known).
However, a Haskell variable with type
var :: AbstractInterface a => a
is *universally* quantified: it means, this variable has a polymorphic value, which can be used as a value of any type which is an instance of AbstractInterface. Here, it is the code which *uses* var that gets to choose its type (and indeed, different choices can be made for different uses); the definition of var cannot choose, and must work for any type which is an instance of AbstractInterface (hence it must only use methods of the AbstractInterface type class).
Writing
a :: Num n => n a = 3 :: Integer
is trying to define a universally quantified value as if it were existentially quantified.
Now, it *is* possible to have existentially quantification in Haskell; I can show you how if you like, but I think I'll stop here for now.
Does this help?
Yes thanks.
From other answers, I also got the essence of it, but now I know the exact terminology: I met the terms 'existentially quantification' and 'universal quantification' before, but did not have any idea of their meaning. I did not now than embarking in studying haskell I had to study phylosophy too :-)
Joking apart, I believe something like your explanation should be reported in tutorials helping to learn haskell, especially the ones for people coming from other languages. As I said in my initial post the analogy 'type class are like interfaces' is a good starting point, but carried too far it is misleading. Thanks again to all the posters that replied to my question. This seems a lively forum: expect to see soon other questions from me :-). Ciao ------ FB

On Fri, Jan 23, 2009 at 05:46:11PM +0100, Francesco Bochicchio wrote:
2009/1/23 Brent Yorgey
Now, it *is* possible to have existentially quantification in Haskell; I can show you how if you like, but I think I'll stop here for now.
Does this help?
Yes thanks.
From other answers, I also got the essence of it, but now I know the exact terminology: I met the terms
Glad it helped! And just so you know, I made a typo; that should say 'existential quantificataion', not 'existentially quantification'. =) -Brent

On 2009 Jan 23, at 8:37, Francesco Bochicchio wrote:
I think I get the polymorphism. What I don't get is why a specialized type cannot replace a more generic type, since the specialized type implements the interface defined in the generic type.
Try it this way. The declaration
a :: Num n => n a = 3 :: Integer
is not the same as
AbstractInterface a = new ConcreteClass();
because Integer doesn't implement *all* of Num, in particular nothing needed for the Float, Double, or Complex instances. In a sense, instances in Haskell are inverses of interfaces in Java; in Java you accept more specific but in Haskell you accept *less* specific. This is because an instance actually asserts a "for all possible matching types" requirement, whereas Java asserts an "any matching type" requirement. (Pedantically:
a :: forall n. Num n => n
You don't have to write (and in Haskell 98, can't write, just as in Java you can't explicitly write "forany" in an interface definition) the "forall"; in Haskell98 (and Java respectively) it's there by definition, in Haskell extensions it's implied for top level types for backward compatibility.) In many cases you can treat the two the same because the member types happen to have an exact relationship such that "forall" and "forany" are both satisfied, but Num is too wide (and far too complex; the ideal structure of the Haskell numeric classes is constantly debated). So the equivalence of typeclasses and interfaces is a "most of the time" thing, not a definition or requirement. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

hi,
2009/1/23 Francesco Bochicchio
Then I discovered that this piece of code (1) is illegal in askell (ghc gives the 'rigid type variable' error)
Num n => a :: n a = 3 :: Integer
I guess you mean: a :: Num n => n The problem whith your implementation of 'a' a = 3 :: Integer is that it provides too specific result. Its type signature says that its result has to be of the type n for *any* instance of the class Num. But your result is simply Integer that is just *one* specific instance of Num. In other words it has to be possible to specialize ("retype") 'a' to any other instance of Num, which is no longer possible because (3 :: Integer) is already specialized.
I also discovered that this (2) instead is legal:
Num n => a :: n a = 3
It's fine because 3 has the type (Num t) => t:: Prelude> :t 3 3 :: (Num t) => t
because it is implicitely translated into (3):
Num n => a :: n a = fromInteger 3
also fine: Prelude> :t fromInteger fromInteger :: (Num a) => Integer -> a Sincerely, jan.

2009/1/23 Jan Jakubuv
hi,
2009/1/23 Francesco Bochicchio
: Then I discovered that this piece of code (1) is illegal in askell (ghc gives the 'rigid type variable' error)
Num n => a :: n a = 3 :: Integer
I guess you mean:
a :: Num n => n
Yes. I'm not _that_ beginner :-) (although I tend to make this mistake quite often ).
The problem whith your implementation of 'a'
a = 3 :: Integer
is that it provides too specific result. Its type signature says that its result has to be of the type n for *any* instance of the class Num. But your result is simply Integer that is just *one* specific instance of Num. In other words it has to be possible to specialize ("retype") 'a' to any other instance of Num, which is no longer possible because (3 :: Integer) is already specialized.
Uhm. Now I think I start to get it ... You are saying that if a value is a Num, it shall be possible to convert it in _any_ of the num instances?
Sincerely, jan.
Ciao ------- FB

2009/1/23 Francesco Bochicchio
2009/1/23 Jan Jakubuv
hi,
2009/1/23 Francesco Bochicchio
: The problem whith your implementation of 'a'
a = 3 :: Integer
is that it provides too specific result. Its type signature says that its result has to be of the type n for *any* instance of the class Num. But your result is simply Integer that is just *one* specific instance of Num. In other words it has to be possible to specialize ("retype") 'a' to any other instance of Num, which is no longer possible because (3 :: Integer) is already specialized.
Uhm. Now I think I start to get it ... You are saying that if a value is a Num, it shall be possible to convert it in _any_ of the num instances?
Well, in this case of the above constant yes. The main point here is that when you want to implement a function of a type say (Num a => a -> a) then the implementation has to work for *all* instances of the class Num. Usually you can use only "abstract" functions defined in a class declaration to write such functions. Try to start with some function that mentions the quantified type in one of its arguments. They are easier to understand. Constants like (Num a => a) and functions like (Num a => Bool -> a) are rare (also they have a special name I can not recall ;-). Also note that you can take: a :: (Num t) => t a = 3 and then specialize it: spec = (a :: Integer) Sincerely, jan.

Francesco Bochicchio
It worked well ... unitil yesterday. Then I discovered that this piece of code (1) is illegal in askell (ghc gives the 'rigid type variable' error)
a :: Num n => n a = 3 :: Integer
A way to understand the type of a is that it has an implicit parameter: a :: {n:Type} -> Num n -> n Now we can see why the definition you gave doesn't work - the type n is provided by the caller of your function which *must* return a value of that type. If your function was called with a {Float} _ 0.0 then you must return a Float. Your definition specifically declares you return an integer, which is wrong. Hope this helps, Eric
participants (9)
-
Andrew Wagner
-
Brandon S. Allbery KF8NH
-
Brent Yorgey
-
Eric Macaulay
-
Ertugrul Soeylemez
-
Francesco Bochicchio
-
Jan Jakubuv
-
Paul Visschers
-
Thomas Davie