Type classes are not like interfaces, after all

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Francesco Bochicchio
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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090123/fea27376/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Paul Visschers-2
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
> [hidden email]
> http://www.haskell.org/mailman/listinfo/beginners

Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Jan Jakubuv-2
In reply to this post by Francesco Bochicchio
hi,

2009/1/23 Francesco Bochicchio <[hidden email]>:
>
> 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.
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Francesco Bochicchio
In reply to this post by Paul Visschers-2
2009/1/23 Paul Visschers <[hidden email]>

>  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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090123/afac5e0d/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Thomas Davie

On 23 Jan 2009, at 14:37, Francesco Bochicchio wrote:

>
>
> 2009/1/23 Paul Visschers <[hidden email]>
> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090123/78b0413c/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Francesco Bochicchio
In reply to this post by Jan Jakubuv-2
2009/1/23 Jan Jakubuv <[hidden email]>

> hi,
>
> 2009/1/23 Francesco Bochicchio <[hidden email]>:
> >
> > 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090123/38a72072/attachment-0001.htm
Reply | Threaded
Open this post in threaded view
|

Re: Type classes are not like interfaces, after all

Ertugrul Söylemez
In reply to this post by Francesco Bochicchio
Francesco Bochicchio <[hidden email]> 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.

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/


Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Brent Yorgey-2
In reply to this post by Francesco Bochicchio
>
> 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
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Andrew Wagner
I think this explanation is positively brilliant. Byorgey++

On Fri, Jan 23, 2009 at 9:59 AM, Brent Yorgey <[hidden email]>wrote:

> >
> > 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
> [hidden email]
> http://www.haskell.org/mailman/listinfo/beginners
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090123/cafabf49/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Jan Jakubuv-2
In reply to this post by Francesco Bochicchio
2009/1/23 Francesco Bochicchio <[hidden email]>:

>
>
> 2009/1/23 Jan Jakubuv <[hidden email]>
>>
>> hi,
>>
>> 2009/1/23 Francesco Bochicchio <[hidden email]>:
>> >
>> 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.
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Francesco Bochicchio
In reply to this post by Brent Yorgey-2
2009/1/23 Brent Yorgey <[hidden email]>

> >
> > 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090123/b196a1e6/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Brent Yorgey-2
On Fri, Jan 23, 2009 at 05:46:11PM +0100, Francesco Bochicchio wrote:

> 2009/1/23 Brent Yorgey <[hidden email]>
>
> > 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
Reply | Threaded
Open this post in threaded view
|

Type classes are not like interfaces, after all

Brandon S Allbery KF8NH
In reply to this post by Francesco Bochicchio
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] [hidden email]
system administrator [openafs,heimdal,too many hats] [hidden email]
electrical and computer engineering, carnegie mellon university    KF8NH


-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090124/e9014272/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Re: Type classes are not like interfaces, after all

Eric-175
In reply to this post by Francesco Bochicchio
Francesco Bochicchio <bieffe62 <at> gmail.com> writes:

>
> 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