HOME

TheInfoList



OR:

In
computer science Computer science is the study of computation, automation, and information. Computer science spans theoretical disciplines (such as algorithms, theory of computation, information theory, and automation) to Applied science, practical discipli ...
, a type class is a
type system In computer programming, a type system is a logical system comprising a set of rules that assigns a property called a type to every "term" (a word, phrase, or other set of symbols). Usually the terms are various constructs of a computer progra ...
construct that supports ad hoc polymorphism. This is achieved by adding constraints to type variables in parametrically polymorphic types. Such a constraint typically involves a type class T and a
type variable In type theory and programming languages, a type variable is a mathematical variable ranging over types. Even in programming languages that allow mutable variables, a type variable remains an abstraction, in the sense that it does not correspond t ...
a, and means that a can only be instantiated to a type whose members support the overloaded operations associated with T. Type classes were first implemented in the Haskell programming language after first being proposed by Philip Wadler and Stephen Blott as an extension to "eqtypes" in
Standard ML Standard ML (SML) is a general-purpose, modular, functional programming language with compile-time type checking and type inference. It is popular among compiler writers and programming language researchers, as well as in the development of ...
, and were originally conceived as a way of implementing overloaded arithmetic and equality operators in a principled fashion. In contrast with the "eqtypes" of Standard ML, overloading the equality operator through the use of type classes in Haskell does not require extensive modification of the compiler frontend or the underlying type system.


Overview

Type classes are defined 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. In Haskell, types can be parameterized; a type class Eq intended to contain types that admit equality would be declared in the following way: class Eq a where (

) :: a -> a -> Bool (/=) :: a -> a -> Bool
where a is one instance of the type class Eq, and a defines the function signatures for 2 functions (the equality and inequality functions), which each take 2 arguments of type a and return a boolean. The type variable a has kind * (* is also known as Type in the latest GHC release), meaning that the kind of Eq is Eq :: Type -> Constraint The declaration may be read as stating a "type a belongs to type class Eq if there are functions named (

)
, and (/=), of the appropriate types, defined on it". A programmer could then define a function elem (which determines if an element is in a list) in the following way: elem :: Eq a => a -> -> Bool elem y [] = False elem y (x:xs) = (x

y) , , elem y xs
The function elem has the type a -> -> Bool with the context Eq a, which constrains the types which a can range over to those a which belong to the Eq type class. (''Note'': Haskell => can be called a 'class constraint'.) A programmer can make any type t a member of a given type class C by using an ''instance declaration'' that defines implementations of all of C's methods for the particular type t. For instance, if a programmer defines a new data type t, they may then make this new type an instance of Eq by providing an equality function over values of type t in whatever way they see fit. Once they have done this, they may use the function elem on /code>, that is, lists of elements of type t. Note that type classes are different from classes in object-oriented programming languages. In particular, Eq is not a type: there is no such thing as a ''value'' of type Eq. Type classes are closely related to parametric polymorphism. For example, note that the type of elem as specified above would be the parametrically polymorphic type a -> -> Bool were it not for the type class constraint "Eq a =>".


Higher-kinded polymorphism

A type class need not take a type variable of kind Type but can take one of any kind. These type classes with higher kinds are sometimes called constructor classes (the constructors referred to are type constructors such as Maybe, rather than data constructors such as Just). An example is the Monad class: class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b The fact that m is applied to a type variable indicates that it has kind Type -> Type, i.e. it takes a type and returns a type, the kind of Monad is thus: Monad :: (Type -> Type) -> Constraint


Multi-parameter type classes

Type classes permit multiple type parameters, and so type classes can be seen as relations on types. For example, in the GHC standard library, the class IArray expresses a general immutable array interface. In this class, the type class constraint IArray a e means that a is an array type that contains elements of type e. (This restriction on polymorphism is used to implement unboxed array types, for example.) Like multimethods, multi-parameter type classes support calling different implementations of a method depending on the types of multiple arguments, and indeed return types. Multi-parameter type classes do not require searching for the method to call on every call at runtime; rather the method to call is first compiled and stored in the dictionary of the type class instance, just as with single-parameter type classes. Haskell code that uses multi-parameter type classes is not portable, as this feature is not part of the Haskell 98 standard. The popular Haskell implementations, GHC and Hugs, support multi-parameter type classes.


Functional dependencies

In Haskell, type classes have been refined to allow the programmer to declare functional dependencies between type parameters—a concept inspired from relational database theory. That is, the programmer can assert that a given assignment of some subset of the type parameters uniquely determines the remaining type parameters. For example, a general
monad Monad may refer to: Philosophy * Monad (philosophy), a term meaning "unit" **Monism, the concept of "one essence" in the metaphysical and theological theory ** Monad (Gnosticism), the most primal aspect of God in Gnosticism * ''Great Monad'', a ...
m which carries a state parameter of type s satisfies the type class constraint Monad.State s m. In this constraint, there is a functional dependency m -> s. This means that for a given monad m of type class Monad.State, the state type accessible from m is uniquely determined. This aids the compiler in
type inference Type inference refers to the automatic detection of the type of an expression in a formal language. These include programming languages and mathematical type systems, but also natural languages in some branches of computer science and linguistic ...
, as well as aiding the programmer in type-directed programming.
Simon Peyton-Jones Simon Peyton Jones (born 18 January 1958) is a British computer scientist who researches the implementation and applications of functional programming languages, particularly lazy functional programming. Education Peyton Jones graduated from ...
has objected to the introduction of functional dependencies in Haskell on grounds of complexity.


Type classes and implicit parameters

Type classes and implicit parameters are very similar in nature, although not quite the same. A polymorphic function with a type class constraint such as: sum :: Num a => -> a can be intuitively treated as a function that implicitly accepts an instance of Num: sum_ :: Num_ a -> -> a The instance Num_ a is essentially a record that contains the instance definition of Num a. (This is in fact how type classes are implemented under the hood by the Glasgow Haskell Compiler.) However, there is a crucial difference: implicit parameters are more ''flexible'' – you can pass different instances of Num Int. In contrast, type classes enforce the so-called ''coherence'' property, which requires that there should only be one unique choice of instance for any given type. The coherence property makes type classes somewhat antimodular, which is why orphan instances (instances that are defined in a module that neither contains the class nor the type of interest) are strongly discouraged. On the other hand, coherence adds an additional level of safety to the language, providing the programmer a guarantee that two disjoint parts of the same code will share the same instance. As an example, an ordered
set Set, The Set, SET or SETS may refer to: Science, technology, and mathematics Mathematics *Set (mathematics), a collection of elements *Category of sets, the category whose objects and morphisms are sets and total functions, respectively Electro ...
(of type Set a) requires a
total order In mathematics, a total or linear order is a partial order in which any two elements are comparable. That is, a total order is a binary relation \leq on some set X, which satisfies the following for all a, b and c in X: # a \leq a ( reflexive ...
ing on the elements (of type a) in order to function. This can be evidenced by a constraint Ord a, which defines a comparison operator on the elements. However, there can be numerous ways to impose a total order. Since set algorithms are generally intolerant of changes in the ordering once a set has been constructed, passing an incompatible instance of Ord a to functions that operate on the set may lead to incorrect results (or crashes). Thus, enforcing coherence of Ord a in this particular scenario is crucial. Instances (or "dictionaries") in Scala type classes are just ordinary values in the language, rather than a completely separate kind of entity. While these instances are by default supplied by finding appropriate instances in scope to be used as the implicit actual parameters for explicitly-declared implicit formal parameters, the fact that they are ordinary values means that they can be supplied explicitly, to resolve ambiguity. As a result, Scala type classes do not satisfy the coherence property and are effectively a syntactic sugar for implicit parameters. This is an example taken from the Cats documentation: // A type class to provide textual representation trait Show // A polymorphic function that works only when there is an implicit // instance of Show available def log a: A)(implicit s: Show = println(s.show(a)) // An instance for String implicit val stringShow = new Show
tring Tring is a market town and civil parish in the Borough of Dacorum, Hertfordshire, England. It is situated in a gap passing through the Chiltern Hills, classed as an Area of Outstanding Natural Beauty, from Central London. Tring is linked to ...
// The parameter stringShow was inserted by the compiler. scala> log("a string") a string
Coq (version 8.2 onwards) also supports type classes by inferring the appropriate instances. Recent versions of Agda 2 also provide a similar feature, called "instance arguments".


Other approaches to operator overloading

In
Standard ML Standard ML (SML) is a general-purpose, modular, functional programming language with compile-time type checking and type inference. It is popular among compiler writers and programming language researchers, as well as in the development of ...
, the mechanism of "equality types" corresponds roughly to Haskell's built-in type class Eq, but all equality operators are derived automatically by the compiler. The programmer's control of the process is limited to designating which type components in a structure are equality types and which type variables in a polymorphic type range over equality types. SML's and
OCaml OCaml ( , formerly Objective Caml) is a general-purpose, multi-paradigm programming language Programming paradigms are a way to classify programming languages based on their features. Languages can be classified into multiple paradigms. ...
's modules and functors can play a role similar to that of Haskell's type classes, the principal difference being the role of type inference, which makes type classes suitable for ''ad hoc'' polymorphism. The object oriented subset of
OCaml OCaml ( , formerly Objective Caml) is a general-purpose, multi-paradigm programming language Programming paradigms are a way to classify programming languages based on their features. Languages can be classified into multiple paradigms. ...
is yet another approach which is somewhat comparable to the one of type classes.


Related notions

An analogous notion for overloaded data (implemented in GHC) is that of type family. In C++ notably C++20 has support for type classes using the
Concepts (C++) Concepts are defined as abstract ideas. They are understood to be the fundamental building blocks of the concept behind principles, thoughts and beliefs. They play an important role in all aspects of cognition. As such, concepts are studied by sev ...
. As an illustration, the above mentioned Haskell example of typeclass Eq would be written as template concept Equal = requires (T a, T b) ; In Clean typeclasses are similar to Haskell, but have a slightly different syntax.
Rust Rust is an iron oxide, a usually reddish-brown oxide formed by the reaction of iron and oxygen in the catalytic presence of water or air moisture. Rust consists of hydrous iron(III) oxides (Fe2O3·nH2O) and iron(III) oxide-hydroxide (FeO( ...
supports traits, which are a limited form of type classes with coherence. Mercury has typeclasses, although they are not exactly the same as in Haskell. In Scala, type classes are a
programming idiom In computer programming, a programming idiom or code idiom is a group of code fragments sharing an equivalent semantic role, which recurs frequently across software projects often expressing a special feature of a recurring construct in one or ...
which can be implemented with existing language features such as implicit parameters, not a separate language feature per se. Because of the way they are implemented in Scala, it is possible to explicitly specify which type class instance to use for a type at a particular place in the code, in case of ambiguity. However, this is not necessarily a benefit as ambiguous type class instances can be error-prone. The proof assistant Coq has also supported type classes in recent versions. Unlike in ordinary programming languages, in Coq, any laws of a type class (such as the monad laws) that are stated within the type class definition, must be mathematically proved of each type class instance before using them.


See also

*
Polymorphism (computer science) In programming language theory and type theory, polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.: "Polymorphic types are types whose opera ...
(other kinds of polymorphism) * Haskell programming language (the language in which type classes were first designed) * Operator overloading (one application of type classes) * Monad (functional programming) (Monad is an example of a type class) *
Concepts (C++) Concepts are defined as abstract ideas. They are understood to be the fundamental building blocks of the concept behind principles, thoughts and beliefs. They play an important role in all aspects of cognition. As such, concepts are studied by sev ...
(since C++20) *
Rust (programming language) Rust is a multi-paradigm, general-purpose programming language. Rust emphasizes performance, type safety, and concurrency. Rust enforces memory safety—that is, that all references point to valid memory—without requiring the use of a ...


References

*


External links

* * Advanced Functional Programming course at Utrecht University, 74 lecture slides o
Advanced Type Classes
2005-06-07.

2014-11-13. {{Data types Functional programming Type theory Data types Articles with example Haskell code