When i was thinking about language design i got an idea that ADTs (Aglebraic Data Types) and typeclasses could be the same thing. They can both represent a group of types, but in haskell they are not the same thing. Typeclass can be extended later, but ADT can’t.
But, sometimes typeclass also cant do everything a ADT can. For example:
class Shape a where
draw :: a -> IO ()
data Rectangle = Rectangle Float Float
instance Shape Rectangle where
draw rectangle = ...
-- no way to put constraint for shape
data Group a = Group [a]
instance Shape Group wheres
draw group = ...
With ADT it’s simple and working, but no new shape can be added:
data Shape
= Rectangle Float Float
| Group [Shape]
draw (Rectangle x y) = ...
draw (Group [ss]) = ...
If classes were more like ADTs, language could behave something like this when it comes to booleans (this is not syntax of a real program):
-- a group of types
class Bool
-- one type with one constructor
data True
-- adding type to group
instance Bool True
data False
instance Bool False
This could also be made with numbers, which would look even more rediculous, but i hope u can get my point.
Is it posible that in other language this two things are one? Does any other language work that way? What are the reasons that ADTs and typeclasses should be seperated?
4
Type classes and Algebraic Data Types are not at all the same thing. They are not quite opposites but have very different purposes
- Type class: defines a set of functions (usually only the signature, with no concrete implementation, although a default implementation can be provided). At any point in code (not just in the relevant module) an instance may be defined to provide a concrete implementation for a specific type, to add it to the type class. Creating this instance can be a temporary convenience and not express anything about the nature and purpose of the class. Adding a type to a type class says nothing about its relationship to other types. Ad hoc Polymorphism is the aim.
- ADT: A very specific composite type created from a set of simpler constructs which together define the different states or structures of the ADT. An ADT may come with no functions beyond the constructors – you could provide a very simple List module that only consisted of data List a = Nil | Cons a (List a) and tell people “Use pattern matching to get the data out, write your own head and tail functions”. Those functions provided with an ADT will be concrete and hard coded to deal specifically with the fixed set of member types. The relationship between the member types is what the ADT defines (authoritatively and finally). The main point is to define the different states of the type expressively in a way that makes it easier to write functions for it while minimising the risk of accidentally creating partial functions and enabling the compiler to optimise wherever pattern matching is exhaustive.
They can both represent a group of types
In the Haskell implementation, an ADT typically consists of just one type – the ADT itself. The set of data constructors which the ADT comprises are values, not types. Bool is a type but nothing can be of type True or False. If you create a Tree type with constructors Empty and Node, nothing can be of type Node or Empty, only of type Tree which can have either an Empty or a Node value. This is a key feature, allowing implementation detail to be encapsulated (if desired) and preventing extension to the set of values (which could break the structure of the type and the functions which work with it). This is pretty much the opposite of the Type Class, where (of course) every member is a type and the set of types which are members of the class is infinitely extensible.
Almost the only thing they have in common is that they are part of the same type system. Neither feature would benefit from acquiring the features of the other. ADTs would be weakened to the point of uselessness (and absurdity) by becoming lose and ad hoc. Type classes would serve no use if they had to be concrete and fixed. That is my answer to your final question.
You suggest no benefit from unifying them. What did you have in mind?
Algebraic Data Types are very common in functional languages. Type classes were invented in Haskell and are much less common – the development communities of the older functional languages have shown no enthusiasm for adopting them. Of the few newer languages which have, I know of none that has tried what you suggest (not even Scala).
6