ChatGPT解决这个技术问题 Extra ChatGPT

Java's Interface and Haskell's type class: differences and similarities?

While I am learning Haskell, I noticed its type class, which is supposed to be a great invention that originated from Haskell.

However, in the Wikipedia page on type class:

The programmer defines a type class by specifying a set of function or constant names, together with their respective types, that must exist for every type that belongs to the class.

Which seems rather close to Java's Interface to me (quoting Wikipedia's Interface(Java) page):

An interface in the Java programming language is an abstract type that is used to specify an interface (in the generic sense of the term) that classes must implement.

These two looks rather similar: type class limit a type's behavior, while interface limit a class' behavior.

I wonder what are the differences and similarities between type class in Haskell and interface in Java, or maybe they are fundamentally different?

EDIT: I noticed even haskell.org admits that they are similar. If they are so similar (or are they?), then why type class is treated with such hype?

MORE EDIT: Wow, so many great answers! I guess I'll have to let the community decide which is the best one. However, while reading the answers, all of them seem to just say that "there are many things typeclass can do while interface cannot or have to cope with generics". I cannot help but wondering, are there anything interfaces can do while typeclasses cannot? Also, I noticed that Wikipedia claims that typeclass was originally invented in the 1989 paper *"How to make ad-hoc polymorphism less ad hoc", while Haskell is still in its cradle, while Java project was started in 1991 and first released in 1995. So maybe instead of typeclass being similar to interfaces, its the other way around, that interfaces were influenced by typeclass? Are there any documents/papers support or disprove this? Thanks for all the answers, they are all very enlightening!

Thanks for all the inputs!

No, there's not really anything interfaces can do that type classes can't, with the major caveat that interfaces generally appear in languages that have built-in features not found in Haskell. Were type classes added to Java, they'd be able to use those features as well.
If you have multiple questions, you should ask multiple questions, not try to cram them all into one question. Anyway, to answer your last question: Java's main influence is Objective-C (and not C++ as is often falsely reported), whose main influences in turn are Smalltalk and C. Java's interfaces are an adaptation of Objective-C's protocols which are again a formalization of the idea of protocol in OO, which in turn is based on the idea of protocols in networking, specifically the ARPANet. This all happened long before the paper you cited. ...
... Haskell's influence on Java came much later, and is limited to Generics, which were, after all, co-designed by one of the designers of Haskell, Phil Wadler.
This is a Usenet article by Patrick Naughton, one of the original designers of Java: Java Was Strongly Influenced by Objective-C and not C++. Unfortunately, it is so old that the orignal posting doesn't even appear in Google's archives.
There's another question that was closed as an exact duplicate of this one, but it has a much more in depth answer: stackoverflow.com/questions/8122109/…

n
newacct

I would say that an interface is kind of like a type class SomeInterface t where all of the values have the type t -> whatever (where whatever does not contain t). This is because with the kind of inheritance relationship in Java and similar languages, the method called depends on the type of object they are called on, and nothing else.

That means it's really hard to make things like add :: t -> t -> t with an interface, where it is polymorphic on more than one parameter, because there's no way for the interface to specify that the argument type and return type of the method is the same type as the type of the object it is called on (i.e. the "self" type). With Generics, there are kinda ways to fake this by making an interface with generic parameter that is expected to be the same type as the object itself, like how Comparable<T> does it, where you are expected to use Foo implements Comparable<Foo> so that the compareTo(T otherobject) kind of has type t -> t -> Ordering. But that still requires the programmer to follow this rule, and also causes headaches when people want to make a function that uses this interface, they have to have recursive generic type parameters.

Also, you won't have things like empty :: t because you're not calling a function here, so it isn't a method.


Scala traits (basically interfaces) allows this.type so you can return or accept parameters of the "self type". Scala has a completely separate feature they call "self types" btw that has nothing to do with this. None of this is a conceptual difference, just implementations difference.
S
Serid

What is similar between interfaces and type classes is that they name and describe a set of related operations. The operations themselves are described via their names, inputs, and outputs. Likewise there may be many implementations of these operations that will likely differ in their implementation.

With that out of the way, here are some notable differences:

Interfaces methods are always associated with an object instance. In other words, there is always an implied 'this' parameter that is the object on which the method is called. All inputs to a type class function are explicit.

An interface implementation must be defined as part of the class that implements the interface. Conversely, a type class 'instance' can be defined completely seperate from its associated type...even in another module.

In general, I think its fair to say that type classes are more powerful and flexible than interfaces. How would you define an interface for converting a string to some value or instance of the implementing type? It's certainly not impossible, but the result would not be intuitive or elegant. Have you ever wished it was possible to implement an interface for a type in some compiled library? These are both easy to accomplish with type classes.


How would you extend typeclasses? Can typeclasses extend other typeclasses just like how interfaces can extend interfaces?
It might be worth updating this answer in light of Java 8's default implementations in interfaces.
@CMCDragonkai Yes, you can for example say "class (Foo a) => Bar a where..." to specify that the Bar type class extends the Foo type class. Like Java, Haskell has multiple inheritance here.
In this case isn't type class same as protocols in Clojure but with type safety?
This makes me wonder how typeclasses differ from Rust traits because those also fulfil both the bullet points
J
Joe the Person

Type classes were created as a structured way to express "ad-hoc polymorphism", which is basically the technical term for overloaded functions. A type class definition looks something like this:

class Foobar a where
    foo :: a -> a -> Bool
    bar :: String -> a

What this means is that, when you use apply the function foo to some arguments of a type that belong to the class Foobar, it looks up an implementation of foo specific to that type, and uses that. This is very similar to the situation with operator overloading in languages like C++/C#, except more flexible and generalized.

Interfaces serve a similar purpose in OO languages, but the underlying concept is somewhat different; OO languages come with a built-in notion of type hierarchies that Haskell simply doesn't have, which complicates matters in some ways because interfaces can involve both overloading by subtyping (i.e., calling methods on appropriate instances, subtypes implementing interfaces their supertypes do) and by flat type-based dispatch (since two classes implementing an interface may not have a common superclass that also implements it). Given the huge additional complexity introduced by subtyping, I suggest it's more helpful to think of type classes as an improved version of overloaded functions in a non-OO language.

Also worth noting is that type classes have vastly more flexible means of dispatch--interfaces generally apply only to the single class implementing it, whereas type classes are defined for a type, which can appear anywhere in the signature of the class's functions. The equivalent of this in OO interfaces would be allowing the interface to define ways to pass an object of that class to other classes, define static methods and constructors that would select an implementation based on what return type is required in calling context, define methods that take arguments of the same type as the class implementing the interface, and various other things that don't really translate at all.

In short: They serve similar purposes, but the way they work is somewhat different, and type classes are both significantly more expressive and, in some cases, simpler to use because of working on fixed types rather that pieces of an inheritance hierarchy.


I was struggling with understanding type hierarchies in Haskell, what do you think of type of types systems like Omega? Could they simulate type hierarchies?
@CMCDragonkai: I'm not familiar enough with Omega to really say, sorry.
c
clay

I've read the above answers. I feel I can answer slightly more clearly:

A Haskell "type class" and a Java/C# "interface" or a Scala "trait" are basically analogous. There is no conceptual distinction between them but there are implementation differences:

Haskell type classes are implemented with "instances" that are separate from the data type definition. In C#/Java/Scala, the interfaces/traits must be implemented in the class definition.

Haskell type classes allow you to return a this type or self type. Scala traits do as well (this.type). Note that "self types" in Scala are a completely unrelated feature. Java/C# require a messy workaround with generics to approximate this behavior.

Haskell type classes let you define functions (including constants) without an input "this" type parameter. Java/C# interfaces and Scala traits require a "this" input parameter on all functions.

Haskell type classes let you define default implementations for functions. So do Scala traits and Java 8+ interfaces. C# can approximate something like this with extensions methods.


Just to add some literature to this answers points, I read On (Haskell) Type Classes and (C#) Interfaces today, which also compares with C# interfaces instead of Javas, but should well give an understanding on the conceptual side dissecting the notion of an interface across language borders.
I think some approximations for things like default implementations are done rather more efficiently in C# with abstract classes perhaps?
l
lambdas

In Master minds of Programming, there's an interview about Haskell with Phil Wadler, the inventor of type classes, who explain the similarities between interfaces in Java and type classes in Haskell:

A Java method like: public static > T min (T x, T y) { if (x.compare(y) < 0) return x; else return y; } is very similar to the Haskell method: min :: Ord a => a -> a -> a min x y = if x < y then x else y

So, type classes are related to interfaces, but the real correspondance would be a static method parametrized with a type as above.


This should be the answer, because according to Phil Wadler's homepage, he was the principle designer of Haskell, at the same time he also designed the Generics extension for Java, which was later included in the language itself.
m
mcandre

Watch Phillip Wadler's talk Faith, Evolution, and Programming Languages. Wadler worked on Haskell and was a major contributor to Java Generics.


The relevant stuff is somewhere around 25m (although the beginning is quite funny).
C
Chris

I can't speak to the "hype"-level, if it seems that way fine. But yes type classes are similar in lots of ways. One difference that I can think of is that it Haskell you can provide behavior for some of the type class's operations:

class  Eq a  where
  (==), (/=) :: a -> a -> Bool
  x /= y     = not (x == y)
  x == y     = not (x /= y)

which shows that there are two operations, equal (==), and not-equal (/=), for things that are instances of the Eq type class. But the not-equal operation is defined in terms of equals (so that you'd only have to provide one), and vice versa.

So in probably-not-legal-Java that would be something like:

interface Equal<T> {
    bool isEqual(T other) {
        return !isNotEqual(other); 
    }

    bool isNotEqual(T other) {
        return !isEqual(other); 
    }
}

and the way that it would work is that you'd only need to provide one of those methods to implement the interface. So I'd say that the ability to provide a sort of partial implemention of the behavior you want at the interface level is a difference.


p
prayagupa

Read Software Extension and Integration with Type Classes where examples are given of how type classes can solve a number of problems that interfaces cannot.

Examples listed in the paper are:

the expression problem,

the framework integration problem,

the problem of independent extensibility,

the tyranny of the dominant decomposition, scattering and tangling.


Link is dead above. Try this one instead.
Well now that one's dead too. Try this one
A
Alexandre C.

They are similar (read: have similar use), and probably implemented similarly: polymorphic functions in Haskell take under the hood a 'vtable' listing the functions associated with the typeclass.

This table can often be deduced at compile time. This is probably less true in Java.

But this is a table of functions, not methods. Methods are bound to an object, Haskell typeclasses are not.

See them rather like Java's generics.


s
sclv

As Daniel says, interface implementations are defined seperately from data declarations. And as others have pointed out, there's a straightforward way to define operations that use the same free type in more than one place. So its easy to define Num as a typeclass. Thus in Haskell we get the syntactic benefits of operator overloading without actually having any magic overloaded operators -- just standard typeclasses.

Another difference is that you can use methods based on a type, even when you don't have a concrete value of that type hanging around yet!

For example, read :: Read a => String -> a. So if you have enough other type information hanging around about how you'll use the result of a "read", you can let the compiler figure out which dictionary to use for you.

You can also do things like instance (Read a) => Read [a] where... which lets you define a read instance for any list of readable things. I don't think that's quite possible in Java.

And all this is just standard single-parameter typeclasses with no trickery going on. Once we introduce multi-parameter typeclasses, then a whole new world of possibilities opens up, and even more so with functional dependencies and type families, which let you embed much more information and computation in the type system.


Normal type classes are to interfaces as multi-parameter type classes are to multiple-dispatch in OOP; you gain a corresponding increase in the power of both the programming language and the programmer's headache.