What I wish someone had told me...

I was just thinking about what I wish someone had told me when I started working with Haskell (not that long ago). It would have saved me a great deal of trouble. The Difference Between Interfaces and Type Classes. Many "Introduction to Haskell for the OOper" style tutorials claim that type classes are like Interfaces (for languages with that feature). I now think that, although this is technically true, it's fundamentally misleading. Here's a simple example demonstrating my line of thought: In C# (and presumably Java), this sort of function is common: public IList GetListOfData() In Haskell, a similar function may be GetListOfData :: (Foldable a, Indexable a) => IO a In C#, it doesn't matter what the actual type returned by GetListOfData is, as long is it supports the IList interface. It's not uncommon for GetListOfData to make a choice between several different implementations, depending on the nature of the data to be returned. The following code is perfectly reasonable in C# : // List and MyList are different classes if (something) { return new List(); } else { return new MyList(); } The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being). So I've come to the conclusion that stating type classes are like interfaces is misleading to newcomers to Haskell, because it leads people to think they should use type classes for the same sorts of problems OO-languages solve with interfaces. In turn this means new programmers are encouraged to use OO-style architecture in programs, reassured that they're in a "functional" idiom because they're using the "functionally-approved" feature of type classes. I think that if I had understood this earlier, I would have embraced functional idioms more quickly. Incidentally, I'm still horrible at designing functional APIs and modules, but at least now I know it, and I'm no longer trying to force OO interfaces into Haskell. So I've made progress.

Well, they act like interfaces in argument types, just not variable or
return types.
Yours, Alexey Romanov
On Tue, Oct 14, 2008 at 4:11 PM, John Lato
I was just thinking about what I wish someone had told me when I started working with Haskell (not that long ago). It would have saved me a great deal of trouble.
The Difference Between Interfaces and Type Classes.
Many "Introduction to Haskell for the OOper" style tutorials claim that type classes are like Interfaces (for languages with that feature). I now think that, although this is technically true, it's fundamentally misleading. Here's a simple example demonstrating my line of thought:
In C# (and presumably Java), this sort of function is common: public IList GetListOfData()
In Haskell, a similar function may be GetListOfData :: (Foldable a, Indexable a) => IO a
In C#, it doesn't matter what the actual type returned by GetListOfData is, as long is it supports the IList interface. It's not uncommon for GetListOfData to make a choice between several different implementations, depending on the nature of the data to be returned. The following code is perfectly reasonable in C# :
// List and MyList are different classes if (something) { return new List(); } else { return new MyList(); }
The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
So I've come to the conclusion that stating type classes are like interfaces is misleading to newcomers to Haskell, because it leads people to think they should use type classes for the same sorts of problems OO-languages solve with interfaces. In turn this means new programmers are encouraged to use OO-style architecture in programs, reassured that they're in a "functional" idiom because they're using the "functionally-approved" feature of type classes. I think that if I had understood this earlier, I would have embraced functional idioms more quickly.
Incidentally, I'm still horrible at designing functional APIs and modules, but at least now I know it, and I'm no longer trying to force OO interfaces into Haskell. So I've made progress. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

You can do equivalent of
// List and MyList are different classes
if (something) { return new List(); }
else { return new MyList(); }
in Haskell as well. But to do that you have to introduce an
existential wrapper in the return type.
In OO languages the existential wrapper is built in to OO constructs,
but in Haskell you have smaller building blocks that you have to
assemble to make the thing you want.
So to extend your example, you can do
data IList = forall a . (Foldable a, Indexable a) => IList a
getListOfData :: IO IList
and now you can return different kinds of types from getListOfData
depending on circumstances.
-- Lennart
On Tue, Oct 14, 2008 at 1:11 PM, John Lato
I was just thinking about what I wish someone had told me when I started working with Haskell (not that long ago). It would have saved me a great deal of trouble.
The Difference Between Interfaces and Type Classes.
Many "Introduction to Haskell for the OOper" style tutorials claim that type classes are like Interfaces (for languages with that feature). I now think that, although this is technically true, it's fundamentally misleading. Here's a simple example demonstrating my line of thought:
In C# (and presumably Java), this sort of function is common: public IList GetListOfData()
In Haskell, a similar function may be GetListOfData :: (Foldable a, Indexable a) => IO a
In C#, it doesn't matter what the actual type returned by GetListOfData is, as long is it supports the IList interface. It's not uncommon for GetListOfData to make a choice between several different implementations, depending on the nature of the data to be returned. The following code is perfectly reasonable in C# :
// List and MyList are different classes if (something) { return new List(); } else { return new MyList(); }
The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
So I've come to the conclusion that stating type classes are like interfaces is misleading to newcomers to Haskell, because it leads people to think they should use type classes for the same sorts of problems OO-languages solve with interfaces. In turn this means new programmers are encouraged to use OO-style architecture in programs, reassured that they're in a "functional" idiom because they're using the "functionally-approved" feature of type classes. I think that if I had understood this earlier, I would have embraced functional idioms more quickly.
Incidentally, I'm still horrible at designing functional APIs and modules, but at least now I know it, and I'm no longer trying to force OO interfaces into Haskell. So I've made progress. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Are you advocating introducing existential types to beginning
Haskellers? I think something with the scary name "existential
quantification" would greatly increase the head'splodin' on the
learnin' slope. Certainly there's a place for them, but I wouldn't
want to see new Haskell programmers habitually approach problems with
a "first create a type class, then make an existential wrapper"
mentality. Which is exactly what I fear is the current situation.
Although my list example is far to shallow to make this point, it
seems to me that it's fairly likely that somebody faced with this
problem has had something go severely wrong at some earlier time.
Existentials are certainly useful, but isn't it also possible that,
for many cases, an alternative design exists which fits a functional
idiom better and doesn't face this issue at all?
John
On Tue, Oct 14, 2008 at 5:03 PM, Lennart Augustsson
You can do equivalent of // List and MyList are different classes if (something) { return new List(); } else { return new MyList(); } in Haskell as well. But to do that you have to introduce an existential wrapper in the return type. In OO languages the existential wrapper is built in to OO constructs, but in Haskell you have smaller building blocks that you have to assemble to make the thing you want.
So to extend your example, you can do data IList = forall a . (Foldable a, Indexable a) => IList a getListOfData :: IO IList and now you can return different kinds of types from getListOfData depending on circumstances.
-- Lennart
On Tue, Oct 14, 2008 at 1:11 PM, John Lato
wrote: I was just thinking about what I wish someone had told me when I started working with Haskell (not that long ago). It would have saved me a great deal of trouble.
The Difference Between Interfaces and Type Classes.
Many "Introduction to Haskell for the OOper" style tutorials claim that type classes are like Interfaces (for languages with that feature). I now think that, although this is technically true, it's fundamentally misleading. Here's a simple example demonstrating my line of thought:
In C# (and presumably Java), this sort of function is common: public IList GetListOfData()
In Haskell, a similar function may be GetListOfData :: (Foldable a, Indexable a) => IO a
In C#, it doesn't matter what the actual type returned by GetListOfData is, as long is it supports the IList interface. It's not uncommon for GetListOfData to make a choice between several different implementations, depending on the nature of the data to be returned. The following code is perfectly reasonable in C# :
// List and MyList are different classes if (something) { return new List(); } else { return new MyList(); }
The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
So I've come to the conclusion that stating type classes are like interfaces is misleading to newcomers to Haskell, because it leads people to think they should use type classes for the same sorts of problems OO-languages solve with interfaces. In turn this means new programmers are encouraged to use OO-style architecture in programs, reassured that they're in a "functional" idiom because they're using the "functionally-approved" feature of type classes. I think that if I had understood this earlier, I would have embraced functional idioms more quickly.
Incidentally, I'm still horrible at designing functional APIs and modules, but at least now I know it, and I'm no longer trying to force OO interfaces into Haskell. So I've made progress. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Tue, 2008-10-14 at 18:15 +0100, John Lato wrote:
Are you advocating introducing existential types to beginning Haskellers? I think something with the scary name
Invalid argument.
"existential quantification" would greatly increase the head'splodin' on the learnin' slope.
Invalid argument. Head explosion is the *goal* of teaching Haskell.
Certainly there's a place for them, but I wouldn't want to see new Haskell programmers habitually approach problems with a "first create a type class, then make an existential wrapper" mentality.
Of course not. That's just translating OO into Haskell. Personally, I would avoid comparing Haskell to other language at all (SOE I believe takes this approach). jcc

On Tue, Oct 14, 2008 at 9:14 PM, Jonathan Cast
On Tue, 2008-10-14 at 18:15 +0100, John Lato wrote:
Are you advocating introducing existential types to beginning Haskellers? I think something with the scary name
Invalid argument.
"existential quantification" would greatly increase the head'splodin' on the learnin' slope.
Invalid argument. Head explosion is the *goal* of teaching Haskell.
Is it? I would certainly prefer my students to say "This is obvious. Why would things work in any other way?" They don't, but I can dream.
Certainly there's a place for them, but I wouldn't want to see new Haskell programmers habitually approach problems with a "first create a type class, then make an existential wrapper" mentality.
Of course not. That's just translating OO into Haskell. Personally, I would avoid comparing Haskell to other language at all (SOE I believe takes this approach).
jcc
I find such comparisons pretty useful. Yours, Alexey Romanov

On Tue, 2008-10-14 at 22:28 +0400, Alexey Romanov wrote:
On Tue, Oct 14, 2008 at 9:14 PM, Jonathan Cast
wrote: On Tue, 2008-10-14 at 18:15 +0100, John Lato wrote:
Are you advocating introducing existential types to beginning Haskellers? I think something with the scary name
Invalid argument.
"existential quantification" would greatly increase the head'splodin' on the learnin' slope.
Invalid argument. Head explosion is the *goal* of teaching Haskell.
Is it? I would certainly prefer my students to say "This is obvious. Why would things work in any other way?"
Sure. But I see head explosion as a means to that end (in the finest tradition of functional languages, from lisp onward).
They don't, but I can dream.
Certainly there's a place for them, but I wouldn't want to see new Haskell programmers habitually approach problems with a "first create a type class, then make an existential wrapper" mentality.
Of course not. That's just translating OO into Haskell. Personally, I would avoid comparing Haskell to other language at all (SOE I believe takes this approach).
I find such comparisons pretty useful.
What kind of comparisons? Translations between languages or comparisons of idioms (which usually involve quite different factorings)? And useful for getting students to be quiet, or useful for getting them to produce idiomatic Haskell? jcc

On Tue, Oct 14, 2008 at 9:15 PM, John Lato
Are you advocating introducing existential types to beginning Haskellers? I think something with the scary name "existential quantification" would greatly increase the head'splodin' on the learnin' slope. Certainly there's a place for them, but I wouldn't want to see new Haskell programmers habitually approach problems with a "first create a type class, then make an existential wrapper" mentality. Which is exactly what I fear is the current situation.
I don't think this is the current situation at all. For one, the beginning Haskellers do _not_ learn about existential types. Yours, Alexey Romanov

I'm not advocating existential types in this case. I rarely use them myself.
I was just pointing out that the mechanism for doing the OO thing
exists in Haskell too, albeit looking a little different.
I don't think there's anything weird about existential types, except
an unfamiliar name.
On Wed, Oct 15, 2008 at 1:15 AM, John Lato
Are you advocating introducing existential types to beginning Haskellers? I think something with the scary name "existential quantification" would greatly increase the head'splodin' on the learnin' slope. Certainly there's a place for them, but I wouldn't want to see new Haskell programmers habitually approach problems with a "first create a type class, then make an existential wrapper" mentality. Which is exactly what I fear is the current situation.
Although my list example is far to shallow to make this point, it seems to me that it's fairly likely that somebody faced with this problem has had something go severely wrong at some earlier time.
Existentials are certainly useful, but isn't it also possible that, for many cases, an alternative design exists which fits a functional idiom better and doesn't face this issue at all?
John
On Tue, Oct 14, 2008 at 5:03 PM, Lennart Augustsson
wrote: You can do equivalent of // List and MyList are different classes if (something) { return new List(); } else { return new MyList(); } in Haskell as well. But to do that you have to introduce an existential wrapper in the return type. In OO languages the existential wrapper is built in to OO constructs, but in Haskell you have smaller building blocks that you have to assemble to make the thing you want.
So to extend your example, you can do data IList = forall a . (Foldable a, Indexable a) => IList a getListOfData :: IO IList and now you can return different kinds of types from getListOfData depending on circumstances.
-- Lennart
On Tue, Oct 14, 2008 at 1:11 PM, John Lato
wrote: I was just thinking about what I wish someone had told me when I started working with Haskell (not that long ago). It would have saved me a great deal of trouble.
The Difference Between Interfaces and Type Classes.
Many "Introduction to Haskell for the OOper" style tutorials claim that type classes are like Interfaces (for languages with that feature). I now think that, although this is technically true, it's fundamentally misleading. Here's a simple example demonstrating my line of thought:
In C# (and presumably Java), this sort of function is common: public IList GetListOfData()
In Haskell, a similar function may be GetListOfData :: (Foldable a, Indexable a) => IO a
In C#, it doesn't matter what the actual type returned by GetListOfData is, as long is it supports the IList interface. It's not uncommon for GetListOfData to make a choice between several different implementations, depending on the nature of the data to be returned. The following code is perfectly reasonable in C# :
// List and MyList are different classes if (something) { return new List(); } else { return new MyList(); }
The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
So I've come to the conclusion that stating type classes are like interfaces is misleading to newcomers to Haskell, because it leads people to think they should use type classes for the same sorts of problems OO-languages solve with interfaces. In turn this means new programmers are encouraged to use OO-style architecture in programs, reassured that they're in a "functional" idiom because they're using the "functionally-approved" feature of type classes. I think that if I had understood this earlier, I would have embraced functional idioms more quickly.
Incidentally, I'm still horrible at designing functional APIs and modules, but at least now I know it, and I'm no longer trying to force OO interfaces into Haskell. So I've made progress. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Wed, 2008-10-15 at 05:39 +0800, Lennart Augustsson wrote:
I'm not advocating existential types in this case. I rarely use them myself. I was just pointing out that the mechanism for doing the OO thing exists in Haskell too, albeit looking a little different.
In general, to encode OO you need quite a bit more than existentials. As you are probably aware, there was a cottage industry in the mid to late '90s working on encodings of OO languages into System F + foo calculi. They just about gave up on a complete encoding until someone figured one out. 'turns out all you needed was recursive bounded existential quantification. Of course, the encoding wasn't any fun to use. There are two morals that you can take out of this. Either: classes/objects are a conflation of ideas that might be more profitably separated apart, or: OO is best approached on its own terms.
I don't think there's anything weird about existential types, except an unfamiliar name.
Agreed. I'm extremely tired of the "I haven't heard this term therefore it must be 'scary' and complicated and beyond me" attitude. Such people need to stop acting like five year old children.

Derek Elkins wrote:
On Wed, 2008-10-15 at 05:39 +0800, Lennart Augustsson wrote:
I don't think there's anything weird about existential types, except an unfamiliar name.
Agreed. I'm extremely tired of the "I haven't heard this term therefore it must be 'scary' and complicated and beyond me" attitude. Such people need to stop acting like five year old children.
Not that it has much to do with the debate, but the attitude you complain about is the exact opposite of the attitude of any five year old children that *I* know (well, my son primarily ;-). -- Dr. Janis Voigtlaender http://wwwtcs.inf.tu-dresden.de/~voigt/ mailto:voigt@tcs.inf.tu-dresden.de

Janis Voigtlaender wrote:
Derek Elkins wrote:
Agreed. I'm extremely tired of the "I haven't heard this term therefore it must be 'scary' and complicated and beyond me" attitude. Such people need to stop acting like five year old children.
Not that it has much to do with the debate, but the attitude you complain about is the exact opposite of the attitude of any five year old children that *I* know (well, my son primarily ;-).
Derek probably meant kids that are three quarters through school ... and thus no longer interesting in anything. :( Regards, apfelmus

Yitzchak Gale wrote:
Derek Elkins wrote:
In general, to encode OO... turns out all you needed was recursive bounded existential quantification.
Do you have a reference for that?
I'm not sure if this is precisely what Derek had in mind, but Bruce, Cardelli, and Pierce did a comparison of various object encodings: http://www.cis.upenn.edu/~bcpierce/papers/compobj.ps It's been a while since I read that paper, but skipping to the end tells me that the approach with recursive types and bounded existentials was the only one to support method override, although it was less attractive on other fronts. -- Karl Mazurak

I suspect that more has been done since 1997. Isn't that pre-Oleg? Karl Mazurak wrote:
Yitzchak Gale wrote:
Derek Elkins wrote:
In general, to encode OO... turns out all you needed was recursive bounded existential quantification.
Do you have a reference for that?
I'm not sure if this is precisely what Derek had in mind, but Bruce, Cardelli, and Pierce did a comparison of various object encodings:
http://www.cis.upenn.edu/~bcpierce/papers/compobj.ps
It's been a while since I read that paper, but skipping to the end tells me that the approach with recursive types and bounded existentials was the only one to support method override, although it was less attractive on other fronts.

I'm not advocating existential types in this case. I rarely use them myself. I was just pointing out that the mechanism for doing the OO thing exists in Haskell too, albeit looking a little different.
I don't think there's anything weird about existential types, except an unfamiliar name.
Perhaps, it helps to make Lennart's point about composable building blocks made more explicit (simplifying slightly): - a function 'f :: a -> IO a' has to accept any type 'a', so it can't know anything about those 'a's - a function 'f :: Interface a => a -> IO a' only has to accept types 'a' that *at least* implement 'Interface', so there's a *lower bound* on the information required about those 'a's, and 'f' can't use any more than that (but since 'a' is exposed to 'f's calling context, it might be fixed there) - a constructor of type '(forall a . Interface a => a) -> SomeType' can only be applied to objects of type 'a' that *at least* implement 'Interface', but if 'SomeType' doesn't mention 'a', the result *at most* implements 'Interface' and 'a' itself gets hidden, so there's an *upper bound* on the information being provided The reference that comes to mind On understanding types, data abstraction, and polymorphism. Luca Cardelli and Peter Wegner. ©1985 Computing Surveys, 17(4):471-522, 1985. http://lucacardelli.name/Bibliography.htm predates Haskell, so any 'splodin' heads can't be blamed on Haskell!-) I wouldn't expose beginning programmers to this level of detail about types, but if you're going to talk about interfaces and types at all, and about type classes in particular, to programmers who are beginning their switch to Haskell, it can't hurt them much to read it. It might even help, and if not, it is at least a concrete basis for more Haskell specific explanations when they start running into those questions. Claus
On Wed, Oct 15, 2008 at 1:15 AM, John Lato
wrote: Are you advocating introducing existential types to beginning Haskellers? I think something with the scary name "existential quantification" would greatly increase the head'splodin' on the learnin' slope. Certainly there's a place for them, but I wouldn't want to see new Haskell programmers habitually approach problems with a "first create a type class, then make an existential wrapper" mentality. Which is exactly what I fear is the current situation.
Although my list example is far to shallow to make this point, it seems to me that it's fairly likely that somebody faced with this problem has had something go severely wrong at some earlier time.
Existentials are certainly useful, but isn't it also possible that, for many cases, an alternative design exists which fits a functional idiom better and doesn't face this issue at all?
John
On Tue, Oct 14, 2008 at 5:03 PM, Lennart Augustsson
wrote: You can do equivalent of // List and MyList are different classes if (something) { return new List(); } else { return new MyList(); } in Haskell as well. But to do that you have to introduce an existential wrapper in the return type. In OO languages the existential wrapper is built in to OO constructs, but in Haskell you have smaller building blocks that you have to assemble to make the thing you want.
So to extend your example, you can do data IList = forall a . (Foldable a, Indexable a) => IList a getListOfData :: IO IList and now you can return different kinds of types from getListOfData depending on circumstances.
-- Lennart
On Tue, Oct 14, 2008 at 1:11 PM, John Lato
wrote: I was just thinking about what I wish someone had told me when I started working with Haskell (not that long ago). It would have saved me a great deal of trouble.
The Difference Between Interfaces and Type Classes.
Many "Introduction to Haskell for the OOper" style tutorials claim that type classes are like Interfaces (for languages with that feature). I now think that, although this is technically true, it's fundamentally misleading. Here's a simple example demonstrating my line of thought:
In C# (and presumably Java), this sort of function is common: public IList GetListOfData()
In Haskell, a similar function may be GetListOfData :: (Foldable a, Indexable a) => IO a
In C#, it doesn't matter what the actual type returned by GetListOfData is, as long is it supports the IList interface. It's not uncommon for GetListOfData to make a choice between several different implementations, depending on the nature of the data to be returned. The following code is perfectly reasonable in C# :
// List and MyList are different classes if (something) { return new List(); } else { return new MyList(); }
The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
So I've come to the conclusion that stating type classes are like interfaces is misleading to newcomers to Haskell, because it leads people to think they should use type classes for the same sorts of problems OO-languages solve with interfaces. In turn this means new programmers are encouraged to use OO-style architecture in programs, reassured that they're in a "functional" idiom because they're using the "functionally-approved" feature of type classes. I think that if I had understood this earlier, I would have embraced functional idioms more quickly.
Incidentally, I'm still horrible at designing functional APIs and modules, but at least now I know it, and I'm no longer trying to force OO interfaces into Haskell. So I've made progress. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

John Lato wrote:
Are you advocating introducing existential types to beginning Haskellers? I think something with the scary name "existential quantification" would greatly increase the head'splodin' on the learnin' slope.
OOP(*) advocates introducing existential types to beginning programmers. Although it uses an easy name "object" and not a technical name "existential", the easy-name honeymoon ends in a few minutes as soon as the teacher demonstrates or the students discover the tricky behaviour and semantics. It results in the same confusion and head explosion. The learning curve is the same struggle struggle explosion struggle then click. (*)Object Obfuscation Pomposity The broken analogy between OOP interfaces and Haskell/Isabell type classes is there because some people insist that all languages should be like mainstream languages. You have heard it, even from reputable leaders and pioneers: "if you know one language, picking up others should be easy, they just differ in syntax".

John Lato wrote:
Are you advocating introducing existential types to beginning Haskellers? I think something with the scary name "existential quantification" would greatly increase the head'splodin' on the learnin' slope. Certainly there's a place for them, but I wouldn't want to see new Haskell programmers habitually approach problems with a "first create a type class, then make an existential wrapper" mentality. Which is exactly what I fear is the current situation.
Although my list example is far to shallow to make this point, it seems to me that it's fairly likely that somebody faced with this problem has had something go severely wrong at some earlier time.
Existentials are certainly useful, but isn't it also possible that, for many cases, an alternative design exists which fits a functional idiom better and doesn't face this issue at all?
John
I think one of the reasons more people don't highlight the differences is that, with all due respect, the differences are often too subtle for OOP programmers. That is, few OOP programmers are taught to think about type theory as deeply as is necessary to see why they're so different[1]. On the one hand, few programmers of any ilk are taught to think deeply about type theory so that's unfair to OOP. But on the other hand OO propaganda is rife with claims that the class/inheritance model of types is The One True Way(tm). I'm not saying this to be rude; there are many OO programmers who do know quite a bit about type theory. However, tutorials for OOP are full of indoctrination about how OO type systems are better than C. In my experience that tends to create OO-programmers who don't question the class/inheritance model or think about what other approaches would look like. Discussions where an OO type system is not assumed typically lead to talking past one another, as here[2]. The idea of defining allomorphic functions which don't use dynamic dispatch and don't use inheritance is difficult to explain without a lot of groundwork to undo OO assumptions. That said, I agree it's a pernicious meme which does disservice to everyone. Though I'm not sure showing people Oleg's handiwork is a gentler introduction either ;) [1] Consider, for example, the question of whether the arguments/value of a function should be covariant or contravariant in subclasses. Java got this wrong for arrays. Their answer seems intuitively right, but this is one area where intuitions are suspect. [2] http://www.reddit.com/r/programming/comments/6xerq/why_type_classes_are_inte... -- Live well, ~wren

On Tue, 2008-10-14 at 13:11 +0100, John Lato wrote:
I was just thinking about what I wish someone had told me when I started working with Haskell (not that long ago). It would have saved me a great deal of trouble.
A recent quote of mine from HWN: * ddarius: Here's the short guide to Haskell for OO programmers: Haskell isn't at all an OO language.
The Difference Between Interfaces and Type Classes.
Many "Introduction to Haskell for the OOper" style tutorials claim that type classes are like Interfaces (for languages with that feature). I now think that, although this is technically true, it's fundamentally misleading.
It's not technically true. Type classes and interfaces a la Java are very fundamentally different neither is remotely capable of doing what the other does. As my quote above suggests, essentially nothing transfers from OO programming to Haskell. Haskell has absolutely no support whatsoever for object-oriented programming. At best, you can try to encode objects.
In C# (and presumably Java), this sort of function is common: public IList GetListOfData()
In Haskell, a similar function may be GetListOfData :: (Foldable a, Indexable a) => IO a
In C#, it doesn't matter what the actual type returned by GetListOfData is, as long is it supports the IList interface. It's not uncommon for GetListOfData to make a choice between several different implementations, depending on the nature of the data to be returned. The following code is perfectly reasonable in C# :
// List and MyList are different classes if (something) { return new List(); } else { return new MyList(); }
The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
So I've come to the conclusion that stating type classes are like interfaces is misleading to newcomers to Haskell, because it leads people to think they should use type classes for the same sorts of problems OO-languages solve with interfaces. In turn this means new programmers are encouraged to use OO-style architecture in programs, reassured that they're in a "functional" idiom because they're using the "functionally-approved" feature of type classes. I think that if I had understood this earlier, I would have embraced functional idioms more quickly.
Incidentally, I'm still horrible at designing functional APIs and modules, but at least now I know it, and I'm no longer trying to force OO interfaces into Haskell. So I've made progress.
I strongly agree with the thrust of your email. This "type classes are kinda like interfaces" meme is horrible. The sooner newcomers realize type classes have nothing to do with object oriented programming, or likely anything they've seen before, the better. Unfortunately, there is a glut of crappy tutorials out there. Good introductions tend to deal with this much better and the best advice I can give is to simply ignore most or all tutorials and read or steer others to more comprehensive and better regarded/reviewed resources. For example, this is a line from RWH: "Typeclasses may look like the objects of object-oriented programming, but they are truly quite different." Also later there is another sidebar along those lines.

Am Mittwoch, 15. Oktober 2008 00:34 schrieb Derek Elkins:
It's not technically true. Type classes and interfaces a la Java are very fundamentally different neither is remotely capable of doing what the other does.
Could you elaborate on that, please? I always understood Java's interfaces to be somewhat similar to type classes (and I learnt the bit of Java I know before I even knew the term "Functional Programming", never really got the whole OO thing though). An interface, I thought, is a contract stating that the classes implementing that interface provide certain operations (obeying some rules). If there's more to interfaces, I'm happily unaware of that :) In what way is that "very fundamentally different" from type classes? As the languages as a whole are fundamentally different, that similarity is of course rather superficial, so I will not say that stressing it is beneficial, but I'm not convinced it is detrimental either.
I strongly agree with the thrust of your email. This "type classes are kinda like interfaces" meme is horrible.
Because of the tendency to confuse OO programmers learning Haskell, or is there a deeper reason? (Answer to this question could be superfluous after addressing the above)
For example, this is a line from RWH: "Typeclasses may look like the objects of object-oriented programming, but they are truly quite different." Also later there is another sidebar along those lines.
Now that is something I wouldn't have dreamt of. If anything, I would relate objects to values (except that objects tend to be mutable). Thanks, Daniel

On Wed, 2008-10-15 at 02:22 +0200, Daniel Fischer wrote:
Am Mittwoch, 15. Oktober 2008 00:34 schrieb Derek Elkins:
It's not technically true. Type classes and interfaces a la Java are very fundamentally different neither is remotely capable of doing what the other does.
Could you elaborate on that, please? I always understood Java's interfaces to be somewhat similar to type classes (and I learnt the bit of Java I know before I even knew the term "Functional Programming", never really got the whole OO thing though). An interface, I thought, is a contract stating that the classes implementing that interface provide certain operations (obeying some rules). If there's more to interfaces, I'm happily unaware of that :) In what way is that "very fundamentally different" from type classes?
There are a lot of difference, for example, interfaces are "types" insofar as you can have a variable of type IList; type classes are not types, there are no variables of type Eq. The fundamental one, however, comes down to what it means to be "object-oriented." One defining property of object-oriented programming is dynamic dispatch. This is the fundamental difference between interfaces and type classes. The instance selection for an interface is done at run-time and this is inherently necessary. The instance (in a different sense) selection for type classes is almost always resolvable statically. In Haskell 98 there is only one case that can't be done simply because doing so would result in infinite code. That case is polymorphic recursion. With extensions we get another case, namely existentials. With existentials we do get something like dynamic dispatch. Nevertheless, the equation: interfaces = type classes + existentials does both interfaces and type classes a huge disservice, and at any rate it's the existential not the type class that results in the properties with which OO programmers are familiar. Indeed, interfaces = existentials + record of functions is a better equation in some respects. Anyways, the examples do the talking: void draw(IEnumerable<IDrawable> drawables) { foreach(IDrawable drawable in drawables) drawable.Draw(); } draw(new IDrawable[] { new Circle(), new Square(), new Star() }); The original motivating example of type classes is Eq. Exactly the problem that type classes were -literally- designed to solve is the usual example of the "binary method problem" in the OO literature. You can see this problem in action in the messy semantics and brokenness and gotchas surrounding the Equals method in C# and Java. Then there are examples that just inherently make no sense in a dynamic dispatch world: Bounded, Read, the showList method of Show. You can, if you want, -encode- various OO things into Haskell including interfaces and even use type classes to do the lifting, but the result is definitely an encoding and it is definitely not as simple as interfaces = type classes

The instance selection for an interface is done at run-time and this is inherently necessary. The instance (in a different sense) selection for type classes is almost always resolvable statically. In Haskell 98
In both cases, the dispatch is inherently dynamic, and in both cases, most dispatches can be resolved at compile-time with sufficient effort. The actual percentage may be quite different, tho. Implementation techniques are also fairly different, and the resulting coding style is also very different, but the two concepts are fundamentally very close to each other. Stefan

The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
I have had an unresolved issue on my stack of Haskell vs Java that I wonder
if your observation explains.
If you notice java generics has all sort of gotchas (e.g.
http://www.ibm.com/developerworks/java/library/j-jtp01255.html). I somehow
don't see this discussion in Haskell. I wonder if haskell's model of
letting the caller determine the result type has advantage in that you don't
have all the complexity you would have if you let the API determine their
types.
daryoush
On Wed, Oct 15, 2008 at 11:45 AM, Stefan Monnier
The instance selection for an interface is done at run-time and this is inherently necessary. The instance (in a different sense) selection for type classes is almost always resolvable statically. In Haskell 98
In both cases, the dispatch is inherently dynamic, and in both cases, most dispatches can be resolved at compile-time with sufficient effort. The actual percentage may be quite different, tho. Implementation techniques are also fairly different, and the resulting coding style is also very different, but the two concepts are fundamentally very close to each other.
Stefan
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Wed, 2008-10-15 at 11:56 -0700, Daryoush Mehrtash wrote:
The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
I have had an unresolved issue on my stack of Haskell vs Java that I wonder if your observation explains.
If you notice java generics has all sort of gotchas (e.g. http://www.ibm.com/developerworks/java/library/j-jtp01255.html). I somehow don't see this discussion in Haskell. I wonder if haskell's model of letting the caller determine the result type has advantage in that you don't have all the complexity you would have if you let the API determine their types.
These look more like unfortunate interactions between generics and the pre-existing Java language than anything else. Covariance isn't really an issue in Haskell, since Haskell lacks sub-typing; the various unfortunate consequences of type erasure in Java are avoided by the fact that Haskell types lack constructors, so the user never expects to be able to conjure up a value of an unknown type. jcc

Would you please explain this a bit more:
the various
unfortunate consequences of type erasure in Java are avoided by the fact
that Haskell types lack constructors, so the user never expects to be
able to conjure up a value of an unknown type.
Thanks,
daryoush
On Wed, Oct 15, 2008 at 12:04 PM, Jonathan Cast
On Wed, 2008-10-15 at 11:56 -0700, Daryoush Mehrtash wrote:
The equivalent won't compile in Haskell, because the actual return type does matter, and *is determined by the calling code*. Our fictional GetListOfData can't return a List or a Mylist depending on some conditional, in fact it can't explicitly return either one at all, because the actual type of the result, as determined by the caller, could be either one, or something else entirely (ignoring the IO bit for the time being).
I have had an unresolved issue on my stack of Haskell vs Java that I wonder if your observation explains.
If you notice java generics has all sort of gotchas (e.g. http://www.ibm.com/developerworks/java/library/j-jtp01255.html). I somehow don't see this discussion in Haskell. I wonder if haskell's model of letting the caller determine the result type has advantage in that you don't have all the complexity you would have if you let the API determine their types.
These look more like unfortunate interactions between generics and the pre-existing Java language than anything else. Covariance isn't really an issue in Haskell, since Haskell lacks sub-typing; the various unfortunate consequences of type erasure in Java are avoided by the fact that Haskell types lack constructors, so the user never expects to be able to conjure up a value of an unknown type.
jcc

Would you please explain this a bit more:
the various unfortunate consequences of type erasure in Java are avoided by the fact that Haskell types lack constructors, so the user never expects to be able to conjure up a value of an unknown type.
Even if Haskell had Java-style constructors, it wouldn't be a problem, since type classes exist independently from any object, so the code that needs the constructor will simply receive it in the corresponding dictionary. Stefan

Hello Daryoush, Wednesday, October 15, 2008, 10:56:39 PM, you wrote:
If you notice java generics has all sort of gotchas (e.g. http://www.ibm.com/developerworks/java/library/j-jtp01255.html). I
large prob;em of OOP languages with generics is interaction between those two types of polymorhism. covariant/contravariant typing is one example. since Haskell lacks OOP classes, it doesn't have such pronblem at all. overall, speaking, pure languages (pure OOP, pure FP, pure LP) is much simpler than ones trying to combine OOP, FP and everything else together. There Is Only One Way To Do It In Haskell ;) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

I am having hard time understanding this statement: Haskell types lack constructors, so the user never expects to be
able to conjure up a value of an unknown type.
I am not sure how say in a Java language a constructor can "conjure up a
value of an unknown type".
daryoush
On Wed, Oct 15, 2008 at 12:55 PM, Bulat Ziganshin wrote: Hello Daryoush, Wednesday, October 15, 2008, 10:56:39 PM, you wrote: If you notice java generics has all sort of gotchas (e.g.
http://www.ibm.com/developerworks/java/library/j-jtp01255.html). I large prob;em of OOP languages with generics is interaction between
those two types of polymorhism. covariant/contravariant typing is one
example. since Haskell lacks OOP classes, it doesn't have such
pronblem at all. overall, speaking, pure languages (pure OOP, pure FP,
pure LP) is much simpler than ones trying to combine OOP, FP and
everything else together. There Is Only One Way To Do It In Haskell ;) --
Best regards,
Bulat mailto:Bulat.Ziganshin@gmail.com

On Wed, 2008-10-15 at 13:01 -0700, Daryoush Mehrtash wrote:
I am having hard time understanding this statement:
Haskell types lack constructors, so the user never expects to be able to conjure up a value of an unknown type.
I am not sure how say in a Java language a constructor can "conjure up a value of an unknown type".
Well, that's the point. It can't, in Haskell or in Java. If you understand that --- that you can't call the default constructor of a class that is not statically known at compile time --- then there's no `gotcha', in Haskell or Java. The gotcha in Java is that every type that actually exists does in fact have a default constructor, and every type of the form Set<T> actually has a copy constructor, and so on. But if T isn't statically known at compile time, you can't call it, even though it's guaranteed to exist. In Java, even if I know nothing else about a class T, I know that Set<T> has a copy constructor. So I can get into the habit of calling the Set<T> copy constructor, without paying attention to whether T is statically known or not --- which breaks when T is a generic parameter. So Haskell lacks the `gotcha' because you never get into the bad habit of assuming every type has a constructor in the first place. jcc

On 16 Oct 2008, at 12:09 pm, Jonathan Cast wrote:
I am not sure how say in a Java language a constructor can "conjure up a value of an unknown type".
Well, that's the point. It can't, in Haskell or in Java. If you understand that --- that you can't call the default constructor of a class that is not statically known at compile time
If you understand that about Java, then you don't understand Java. Java reflection means that compile-time types are backed up by runtime objects belonging to Type in general, to Class if they are class types. It also means that you can discover the default constructor by using aClass.getConstructor(), and you can invoke it by using .newInstance(). If it were not possible to do this, Java would not get much use out of its ability to load new classes at run time.

On Thu, 2008-10-16 at 15:02 +1300, Richard O'Keefe wrote:
On 16 Oct 2008, at 12:09 pm, Jonathan Cast wrote:
I am not sure how say in a Java language a constructor can "conjure up a value of an unknown type".
Well, that's the point. It can't, in Haskell or in Java. If you understand that --- that you can't call the default constructor of a class that is not statically known at compile time
If you understand that about Java, then you don't understand Java.
God, I hope never to understand Java. *shudder*
Java reflection means that compile-time types are backed up by runtime objects belonging to Type in general, to Class if they are class types. It also means that you can discover the default constructor by using aClass.getConstructor(), and you can invoke it by using .newInstance().
Wait, what? Why can't Java use this to keep template parameters around at run time? Or is the article (as per which Set<Integer> and Set<Double> are identical at run time) full of it? jcc

On Thu, 2008-10-16 at 15:02 +1300, Richard O'Keefe wrote:
On 16 Oct 2008, at 12:09 pm, Jonathan Cast wrote:
I am not sure how say in a Java language a constructor can "conjure up a value of an unknown type".
Well, that's the point. It can't, in Haskell or in Java. If you understand that --- that you can't call the default constructor of a class that is not statically known at compile time
If you understand that about Java, then you don't understand Java.
God, I hope never to understand Java. *shudder*
Java reflection means that compile-time types are backed up by runtime objects belonging to Type in general, to Class if they are class types. It also means that you can discover the default constructor by using aClass.getConstructor(), and you can invoke it by using .newInstance().
Wait, what? Why can't Java use this to keep template parameters around at run time? Or is the article (as per which Set<Integer> and Set<Double> are identical at run time) full of it?
The article (whichever it was) wasn't full of it... Set<Integer> and Set<Double> are identical at runtime. You cannot, given a Class object at runtime that happens to be the Class object corresponding to Set>, conjure up an instance of Set... but simply for the reason that Set has no constructors (it is an interface). You can, however, given a class object that happens to be the class object corresponding to (say) HashSet, conjure up an instance of a HashSet, and assign it to a variable of the (static) type Set<Integer> or Set<Double>... i.e. Set<Integer> foo = (Set<Integer>) hashSetClass.newInstance(); Set<Double> bar = (Set<Double>) hashSetClass.newInstance(); which will generate warnings about unsafe casts, but nevertheless can compile, and won't cause any exceptions at runtime.

On Thu, 2008-10-16 at 09:48 -0700, Robert Greayer wrote:
On Thu, 2008-10-16 at 15:02 +1300, Richard O'Keefe wrote:
On 16 Oct 2008, at 12:09 pm, Jonathan Cast wrote:
I am not sure how say in a Java language a constructor can "conjure up a value of an unknown type".
Well, that's the point. It can't, in Haskell or in Java. If you understand that --- that you can't call the default constructor of a class that is not statically known at compile time
If you understand that about Java, then you don't understand Java.
God, I hope never to understand Java. *shudder*
Java reflection means that compile-time types are backed up by runtime objects belonging to Type in general, to Class if they are class types. It also means that you can discover the default constructor by using aClass.getConstructor(), and you can invoke it by using .newInstance().
Wait, what? Why can't Java use this to keep template parameters around at run time? Or is the article (as per which Set<Integer> and Set<Double> are identical at run time) full of it?
The article (whichever it was) wasn't full of it... Set<Integer> and Set<Double> are identical at runtime. You cannot, given a Class object at runtime that happens to be the Class object corresponding to Set>, conjure up an instance of Set... but simply for the reason that Set has no constructors (it is an interface).
I think we've found our difference between Haskell and Java... So Set<Double> is an interface, thus you can't copy construct it. Makes sense --- but does it belong in an article about gotchas with *generics*?
You can, however, given a class object that happens to be the class object corresponding to (say) HashSet,
Can I have HashSet<Integer>? Could I construct HashSet>, if I did?
conjure up an instance of a HashSet, and assign it to a variable of the (static) type Set<Integer> or Set<Double>... i.e.
Set<Integer> foo = (Set<Integer>) hashSetClass.newInstance(); Set<Double> bar = (Set<Double>) hashSetClass.newInstance();
which will generate warnings about unsafe casts, but nevertheless can compile, and won't cause any exceptions at runtime.
jcc

--- On Thu, 10/16/08, Jonathan Cast
Can I have HashSet<Integer>? Could I construct HashSet>, if I did?
Yes: HashSet<?> blah = (HashSet<?>) hashSetClass.newInstance(); ... compiles, and won't throw an exception if hashSetClass truly is the class object for HashSet. Pretty useless, because you can't put anything *into* a HashSet> object... blah.add("foo"); // doesn't typecheck... but you can do: HashSet<String> blah = (HashSet<String>) hashSetClass.newInstance(); blah.add("foo"); works fine..

On Thu, 2008-10-16 at 10:02 -0700, Robert Greayer wrote:
--- On Thu, 10/16/08, Jonathan Cast
wrote: Can I have HashSet<Integer>? Could I construct HashSet>, if I did?
Yes:
HashSet<?> blah = (HashSet<?>) hashSetClass.newInstance();
... compiles, and won't throw an exception if hashSetClass truly is the class object for HashSet.
But I can't say new HashSet>()?
Pretty useless, because you can't put anything *into* a HashSet> object...
Even if my client hands my a ? object?
blah.add("foo"); // doesn't typecheck...
but you can do: HashSet<String> blah = (HashSet<String>) hashSetClass.newInstance(); blah.add("foo");
works fine..
jcc

--- On Thu, 10/16/08, Jonathan Cast
But I can't say new HashSet>()?
No... but you can say 'new HashSet<T>()' where T is a type variable, and then put a value of type T into your set, which is probably generally what you want. HashSet<?> is a set of unknown (at compile time) element type. It is not safe to put any element into such a set. Consider: void wrong(List<?> foo, List<?> bar) { foo.add(bar.get(0)); // illegal... but if it weren't... } ... List<Integer> x = ...; List<String> y = ...; wrong(x, y); // then this would put an String into a list of ints... --- Perhaps there was confusion over what you meant by 'conjure up a value of an unknown type'... you can't explicitly instantiate a parameterized class with a wildcard type variable (e.g. new HashSet<?>). However, you can conjure up an instance of any class for which you have a Class object handy, provided it is non-abstract and has public constructors, and then assign it to a variable with a wildcard in its type.

On Thu, 2008-10-16 at 11:41 -0700, Robert Greayer wrote:
--- On Thu, 10/16/08, Jonathan Cast
wrote: But I can't say new HashSet>()?
No... but you can say 'new HashSet<T>()' where T is a type variable, and then put a value of type T into your set, which is probably generally what you want. HashSet> is a set of unknown (at compile time) element type. It is not safe to put any element into such a set. Consider:
void wrong(List<?> foo, List<?> bar) { foo.add(bar.get(0)); // illegal... but if it weren't... }
So if I say void wrong(List<?> foo, List<?> bar) I get two /different/ type variables implicitly filled in? If I declare a generic class, and then have a method, is there a way, in that method's parameter list, to say `the type parameter that was supplied when my class was instantiated'?
... List<Integer> x = ...; List<String> y = ...; wrong(x, y); // then this would put an String into a list of ints...
Yikes. So, in this instance, the difference between Haskell and Java is: if you want to disallow that call to wrong, in Haskell you can...
---
Perhaps there was confusion over what you meant by 'conjure up a value of an unknown type'... you can't explicitly instantiate a parameterized class with a wildcard type variable (e.g. new HashSet>).
Right. Which is a logical consequence of parametric polymorphism.
However, you can conjure up an instance of any class for which you have a Class object handy, provided it is non-abstract and has public constructors, and then assign it to a variable with a wildcard in its type.
(Which is a logical consequence of non-parametric polymorphism, concerning which: yikes!) jcc

--- On Thu, 10/16/08, Jonathan Cast
So if I say
void wrong(List<?> foo, List<?> bar)
I get two /different/ type variables implicitly filled in?
If I declare a generic class, and then have a method, is there a way, in that method's parameter list, to say `the type parameter that was supplied when my class was instantiated'?
Yes - class Foo<T> { ... void right(List<T> foo, List<T> bar) { foo.add(bar.get(0)); } Can also do it at the method level... void <T> alsoRight(List<T> foo, List<T> bar) { ... }
Yikes. So, in this instance, the difference between Haskell and Java is: if you want to disallow that call to wrong, in Haskell you can...
Not exactly... Java disallows 'wrong' from being written (without class casts and such), because it disallows calling a method which has a wildcard type in a contravariant position. IIRC, Scala handles all this more elegantly. If you stay away from '?', and stick to type variables with Java generics, though, how type checking with generics works in Java should be mostly unsurprising to a Haskeller.

On Thu, 2008-10-16 at 12:27 -0700, Robert Greayer wrote:
--- On Thu, 10/16/08, Jonathan Cast
wrote: So if I say
void wrong(List<?> foo, List<?> bar)
I get two /different/ type variables implicitly filled in?
If I declare a generic class, and then have a method, is there a way, in that method's parameter list, to say `the type parameter that was supplied when my class was instantiated'?
Yes - class Foo<T> { ... void right(List<T> foo, List<T> bar) { foo.add(bar.get(0)); }
Can also do it at the method level...
void <T> alsoRight(List<T> foo, List<T> bar) { ... }
Yikes. So, in this instance, the difference between Haskell and Java is: if you want to disallow that call to wrong, in Haskell you can...
Not exactly... Java disallows 'wrong' from being written (without class casts and such), because it disallows calling a method which has a wildcard type in a contravariant position. IIRC, Scala handles all this more elegantly. If you stay away from '?', and stick to type variables with Java generics, though, how type checking with generics works in Java should be mostly unsurprising to a Haskeller.
Oh, good. Daryoush Mehrtash: It looks like the answer to your original question --- gotchas with Java generics vs. Haskell polymorphism --- is that the `gotchas' you linked to are consequent on using ? in your code, instead of true type variables. So the truly problematic construct is ?, and the difference is just that Haskell doesn't have an analogue to it. jcc

So does this mean that the reason for complexity of generics is the Java
inheritance?
BTW, in addition to the article I posted, This site:
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html has a FAQ
on Java generics that is 500+ pages long! In Haskell you have
parametrized types but I don't think you need 500+ page faq to explain it.
daryoush
On Thu, Oct 16, 2008 at 12:27 PM, Jonathan Cast
On Thu, 2008-10-16 at 12:27 -0700, Robert Greayer wrote:
--- On Thu, 10/16/08, Jonathan Cast
wrote: So if I say
void wrong(List<?> foo, List<?> bar)
I get two /different/ type variables implicitly filled in?
If I declare a generic class, and then have a method, is there a way, in that method's parameter list, to say `the type parameter that was supplied when my class was instantiated'?
Yes - class Foo<T> { ... void right(List<T> foo, List<T> bar) { foo.add(bar.get(0)); }
Can also do it at the method level...
void <T> alsoRight(List<T> foo, List<T> bar) { ... }
Yikes. So, in this instance, the difference between Haskell and Java is: if you want to disallow that call to wrong, in Haskell you can...
Not exactly... Java disallows 'wrong' from being written (without class casts and such), because it disallows calling a method which has a wildcard type in a contravariant position. IIRC, Scala handles all this more elegantly. If you stay away from '?', and stick to type variables with Java generics, though, how type checking with generics works in Java should be mostly unsurprising to a Haskeller.
Oh, good.
Daryoush Mehrtash:
It looks like the answer to your original question --- gotchas with Java generics vs. Haskell polymorphism --- is that the `gotchas' you linked to are consequent on using ? in your code, instead of true type variables. So the truly problematic construct is ?, and the difference is just that Haskell doesn't have an analogue to it.
jcc
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Thu, 2008-10-16 at 13:53 -0700, Daryoush Mehrtash wrote:
So does this mean that the reason for complexity of generics is the Java inheritance?
BTW, in addition to the article I posted, This site: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html has a FAQ on Java generics that is 500+ pages long! In Haskell you have parametrized types but I don't think you need 500+ page faq to explain it.
All you need is a T-shirt: http://www.cafepress.com/skicalc

On Thu, 2008-10-16 at 16:25 -0500, Derek Elkins wrote:
On Thu, 2008-10-16 at 13:53 -0700, Daryoush Mehrtash wrote:
So does this mean that the reason for complexity of generics is the Java inheritance?
BTW, in addition to the article I posted, This site: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html has a FAQ on Java generics that is 500+ pages long! In Haskell you have parametrized types but I don't think you need 500+ page faq to explain it.
All you need is a T-shirt: http://www.cafepress.com/skicalc
I think the same explanation would work for Java, too, except: * Many more people know Java than know generics (or this was true at one time), leading to * A substantial market for teaching generics as a separate language feature. I think the market for pedagogical material for teaching polymorphism (exclusively) to Haskellers is much smaller. Instead, polymorphism is taught as an integral part of the language, to people who can't be said to have mastered Haskell yet at all. So I don't think counting page sizes of documents associated to the two language features gives a fair comparison of there complexity. jcc

derek.a.elkins:
On Thu, 2008-10-16 at 13:53 -0700, Daryoush Mehrtash wrote:
So does this mean that the reason for complexity of generics is the Java inheritance?
BTW, in addition to the article I posted, This site: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html has a FAQ on Java generics that is 500+ pages long! In Haskell you have parametrized types but I don't think you need 500+ page faq to explain it.
All you need is a T-shirt: http://www.cafepress.com/skicalc
Indeed, implementing "generics" is an undergraduate exercise, also, https://cgi.cse.unsw.edu.au/~cs3161/08s2/cgi-bin/moin.cgi/Assignment%202 And the full spec. fits in one page (or one tshirt, if that's your preferred medium for typing rules). -- Don

Derek Elkins wrote:
All you need is a T-shirt: http://www.cafepress.com/skicalc

On 17 Oct 2008, at 9:53 am, Daryoush Mehrtash wrote:
So does this mean that the reason for complexity of generics is the Java inheritance?
No. The reason for the complexity of generics in Java is that they weren't designed into the language in the first place. It took several attempts and quite a lot of work to come up with a version of generics that was 100% interoperable with earlier JVMs *and* still worth having. The central fact about Java generics is "erasure semantics"; every Java generic class is equivalent to (and is compiled as if it were) a Java class without any generics at all. One has the odd result that there is elaborate type checking for Java collection classes all of which can be bypassed by linking it with code compiled in Java 1.4 mode, so that all the run-time type checking *still* has to be done. You could have something very like Java generics but without the strangeness if you designed it into the language from the beginning, as Betrand Meyer did with Eiffel. Of course, what _he_ didn't design in from the beginning was lambdas, now present as "agents". Eiffel has its own kinds of strangeness.

On 16 Oct 2008, at 9:01 am, Daryoush Mehrtash wrote:
I am not sure how say in a Java language a constructor can "conjure up a value of an unknown type".
... Class anUnknownClass; Object anInstance; anInstance = anUnknownClass.getConstructor().newInstance(); If you know that the constructor will require arguments of types T1 and T2, and you have suitable values v1, v2, anInstance = anUnknownClass.getConstructor(T1, T2).newInstance(v1, v2); will do the job.

Daryoush Mehrtash wrote:
I have had an unresolved issue on my stack of Haskell vs Java that I wonder if your observation explains.
If you notice java generics has all sort of gotchas (e.g. http://www.ibm.com/developerworks/java/library/j-jtp01255.html). I somehow don't see this discussion in Haskell. I wonder if haskell's model of letting the caller determine the result type has advantage in that you don't have all the complexity you would have if you let the API determine their types.
That "gotcha" about generics, is really a gotcha about arrays. If you replace List<> with [] in their example it'll compile just fine, though it can never work. The runtime happens to catch this particular error as an ArrayStoreException, but there are more convoluted examples that let you break the type system in all sorts of disturbing ways. As I said before, co-/contravariance is a Hard Concept(tm) where intuitions lie. I think the big difference is that Haskell (similar to some non-Java OO languages) separates the notion of a value from the notion of associated functions. In Haskell we expect that the result of a function, say, is a value with an actual type-- the actual type needed by the caller in fact, perhaps constructed by a type-class dictionary passed down by the caller. Whereas in Java the result of a function is some stateful bundle of functions-- and whatever the type is, the callee returns the functions that can be used on it. This is highlighted in Haskell by type signatures like |Foo a => a|, sure the type of our result may have a bundle of associated functions, but we know that the result is really a value of type |a|, polymorphically if need be. For simple object-like types that may not seem so different, but for container types the difference becomes readily apparent. In Java an interface needs to cement the collection and so you often see interfaces with different versions of the same function only offering/accepting different collections. There are some abstract classes or interfaces that try to expand on this to make things more polymorphic, but this perspective still can't escape certain problems... For example, the fragile base class problem. One of my gripes about Java is that for some unfathomable reason, Iterators are not Iterable. More particularly, for iterators from library collections, I often cannot make them so[1]. In Haskell, since associated functions like iterator() are not part of the value but instead exist ethereally, we'd be free to give an instance for all Iterators making them Iterable. This is the sort of problem that Pythonistas and Rubyists work around with monkey patching. Another example is, say we have an object that constructs a priority queue internally, and ultimately hands it off to the caller; this class doesn't actually care about the pqueue itself, though it doesn't treat it as a black box either. If someone comes up with a more efficient implementation of pqueues they can't just require it (via polymorphism) from that class, instead they need to go in and change the class that doesn't care[2]. In a sense, Haskell embraces the dread spectre of multiple inheritance. There are inheritance trees of type-class instances, but the association of these functions to datatypes is flat, and datatypes can always opt into additional type-classes. The diamond problem of C++ multiple inheritance goes away since values never inherit additional fields, however we do still have diamond problems of our own which OverlappingInstances and IncoherentInstances try to work around. Type-classes do specify types in the API, they can even specify types in ways that Java interfaces cannot (e.g. |a -> a -> b| [3]). But the way they specify types, functional polymorphism vs inheritance, are radically different. [1] Cf. Vector which uses an anonymous local class. In order to have Vectors that do return iterable iterators, we need to subclass Vector and override iterator() with an almost identical declaration that adds a method: public Iterator<E> iterator() { return this; } [2] Potentially by subclassing, assuming the class doesn't have too many interdependencies. Or by using some defunctionalization pattern to mimic caller-directed polymorphism, especially if we want to reuse the same factory for multiple callers for whom different pqueues are more efficient. [3] Java has only one top type: Object, and so it can't ensure this constraint. In some cases generics can be bent to fill this gap, but in others it's much harder, e.g. |class Foo a where foo :: Int -> a|. OO-Languages like SmallTalk can better capture some of these because they treat classes as objects too, but there are still differences. -- Live well, ~wren

On Wed, 2008-10-15 at 14:45 -0400, Stefan Monnier wrote:
The instance selection for an interface is done at run-time and this is inherently necessary. The instance (in a different sense) selection for type classes is almost always resolvable statically. In Haskell 98
In both cases, the dispatch is inherently dynamic, and in both cases, most dispatches can be resolved at compile-time with sufficient effort. The actual percentage may be quite different, tho. Implementation techniques are also fairly different, and the resulting coding style is also very different, but the two concepts are fundamentally very close to each other.
Rewrite this code so that there is no run-time remnants of dynamic dispatch, that is to say there is no run-time method look-up of any sort. You can assume that this plus an interface declaration for IDrawable and two classes Square and Circle is the whole program. int n = int.Parse(System.Console.ReadLine()); List<IDrawable> drawables = new List<IDrawable>(); for(int i = 0; i < n; ++i) drawables.Add(new Square()); drawables.Add(new Circle()); foreach(IDrawable drawable in drawables) drawable.Draw(); And exercise two: Write a Haskell example using type classes and not using existentials or polymorphic recursion where given the whole program dispatch is inherently dynamic.
participants (21)
-
Albert Y. C. Lai
-
Alexey Romanov
-
apfelmus
-
Bulat Ziganshin
-
Claus Reinke
-
Dan Weston
-
Daniel Fischer
-
Daryoush Mehrtash
-
Derek Elkins
-
Don Stewart
-
Janis Voigtlaender
-
John Lato
-
Jonathan Cast
-
Karl Mazurak
-
Lennart Augustsson
-
Paul Johnson
-
Richard O'Keefe
-
Robert Greayer
-
Stefan Monnier
-
wren ng thornton
-
Yitzchak Gale