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?

  • 5
    I expect that making algebraic data types open, you not only cripple a number of optimizations and sanity checks, but cause modularity of the system to break down. How does one module know that another has added a new option for example? But I don't know for sure. Others hopefully can answer more authoritatively. – Telastyn Feb 7 '15 at 16:18
  • @Telastyn there would be a possibility of making them closed. For example bool must be closed, bcs u don't want to make all type matching not complete. They would not be defined the same way, just used like they are the same thing. – ivokosir Feb 9 '15 at 11:04
  • 1
    If you can arbitrarily add values to an ADT, you instantly break most of the functions already written to work with them. There is no gain there. It isn't a sane feature to implement in a language. – itsbruce Feb 9 '15 at 17:01
  • The most important thing that breaks if you allowing adding new cases to an ADT is pattern matching -- suddenly, there's no longer a guarantee that existing code has a pattern for every possible input, so there's a possibility of runtime failure. – Jules Apr 11 '15 at 0:30

Type classes and Algebraic Data Types are not at all the same thing. They are not quite opposites but have very different purposes

  1. 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.
  2. 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).

  • Yes, that is the way it's in Haskell, but I am not asking why is Haskell designed that way, just is that way of thinking about data even possible. I got this idea when started learning Rust, but I wanted to make the example in Haskell because I am still new to Rust. In Rust Traits are still types, and the Struct itself is a type which can be inside a Enum that is also a type. The benefit would be that u don't need to think about what to use when creating some structure like in my question. – ivokosir Feb 9 '15 at 11:16
  • Note that I don't think type like bool could be extended. The way i think about it is that bool is in a way the same as car. Bool can be true and false, while car can be lamborgnihi, audi, but also many more. – ivokosir Feb 9 '15 at 11:17
  • Rust already has Algebraic Data Types - enums. No traits needed for their construction. Why would you not think about whether something is an ADT or a trait? It's a very simple decision about things with very clear properties. It's a bit like saying "I don't want to think about whether this is a boolean or a list of strings yet" - clearly different things with different purposes. – itsbruce Feb 9 '15 at 13:25
  • You know in Rust you can add a trait to any other type including an ADT? (Same as in Haskell, where you can create an instance to add any ADT to any type class, if you can think of function implementations that will work). So you can create a set of behaviour in a trait and integrate it with an ADT any time you want. – itsbruce Feb 9 '15 at 13:29
  • I am aware that ADT ~= enum and trait ~= typeclass. I think I understood the problem. One can't, for instance, implement every pattern match when creating a new type, so the programming language couldn't have pattern matching at all... and would yet again need uncool bools. Removing pattern matching would cripple ADTs, but would that make them kind of the same? – ivokosir Feb 9 '15 at 14:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.