As I understand the Type Class is that it's not something concrete but just a construct for ad-hoc and parametric polymorphism. Eq and Semigroup are examples of type classes. On the other hand, there is Algebraic data type, which is a concrete composite type, for example, Either and Maybe. And they are also Functors.
So, there is a specification for Algebraic data types for JavaScript: https://github.com/fantasyland/fantasy-land. On this page, Setoid(Eq), Ord and Semigroup are also ADT. But is it correct? If so, they are composite of what types?
I also find out this article regarding type classes, and here Functors and Monads are type classes. https://typelevel.org/cats/typeclasses.html#type-classes-in-cats. And, is it means that Either and Maybe are type classes as well?
Is Eq a type class or algebraic data type? Or both? Same for Functor
CodePudding user response:
The underlying root of your confusion is distinguishing concepts from implementations.
So let's try to make some of those clearer.
Typeclasses
Is a pattern to achieve polymorphism; punctually, ad-hoc polymorphism.
In some languages those are a feature provided by the language; e.g. Haskell or Rust.
Others, allow you to express them using other features; e.g. Scala (using implicits).
And finally, in theory, you may model them in any language, but you would need to pass the instances manually, this is what is usually called the strategy pattern; an example is Java's Comparator
. Some folks argue that if the value has to be passed explicitly then is not a typeclass, I personally disagree but let's not open that pandora's box.
Algebraic Data Types (ADTs)
Is a pattern to model data based on simple constructors known as products and unions.
Again, some languages provide the building blocks for those out of the box, like Haskell. Some others emulate them upon classic subtyping and objects with some help from the compiler, like Scala.
And, once again, you may in theory model them in any language and replace pattern matching with a fold
. But, in this case, I do agree that if the UX is not pleasant and you don't have pattern matching built-in in the language is hard to talk about ADTs.
Functor
Is an abstraction, a concept derived from category theory.
In essence, it is constituted of three things:
- A type
F[_]
of kind* -> *
(commonly know as type constructors); e.g.List
- An implementation of
map[A, B](fa: F[A])(f: A => B): F[B]
for the given typeF
- A formal proof that such implementation satisfies the
Functor
laws.
I won't attempt to explain what a Functor
is here...
But, a quick TL;DR; is that it represents the possibility of transforming some context by applying a function to the value(s) it will compute.
Since a Functor
is an abstraction, we need some form of polymorphism to represent it.
Experience has shown us that typeclasses are the most natural way to model Functor
, Monad
, Monoid
, etc. But, you may try to use other approaches like structural types or subtyping... but really, you will hit a wall sooner than latter.
Extras
On top of all that, we also need to add other layers of indirection that each language may have.
For example, in Scala we use subtyping to model typeclasses hierarchies, so Monad[F] extends Functor[F]
, and the same keyword to model both subtyping abstractions and typeclass abstractions trait
.
So again, it is important to always separate concepts / ideas from their underlying implementations; at the end of the day, everything will be just electrical pulses on a weird machine :p
CodePudding user response:
ADTs and typeclasses are both "things with multiple types", and I think that's what's confusing you. I think the reason we're having a hard time answering your question (or even figuring out precisely what it is) is because other than that they have almost nothing in common.
A typeclass could be thought of as a contract or specification. It exists independently of any concrete type and they are open: and any type you come up with could implement the necessary details to be a member of the set. We specify this in different ways in different languages that include this concept.
For instance Rust traits like Display
:
use std::fmt;
struct Foo(i32);
impl fmt::Display for Foo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
Or Haskell typeclases like Show
:
data Foo = Int deriving Show
We have created in both cases a very basic type Foo
based on the integer type that implements the "has a string representation" typeclass in the respective languages. As I said before the set of types that implement Show/Display
is open: we can do this forever with any applicable type we come up with.
Contrast that with Maybe
, which is a tagged union (which qualifies it as an ADT) Just a | Nothing
for some type a
. The set of types in the Maybe
union is closed: you can use any type a
you want but you can't add new things to the union.
Maybe
is a type, not a typeclass. It's meaningless to say that Foo
implements/derives Maybe
. Maybe
is not a set of rules for some other type to follow, anything could be a Maybe Whatever
.
But Maybe
as a type does implement some typeclasses, for instance the Monad
typeclass (and by implication Functor
, Applicative
, etc.). It is not meaningless to say that Maybe
implements the Monad
specification.