Jump to content

Type class

From Wikipedia, the free encyclopedia

Incomputer science,atype classis atype systemconstruct that supportsad hoc polymorphism.This is achieved by adding constraints to type variables inparametrically polymorphictypes. Such a constraint typically involves a type classTand atype variablea,and means thatacan only be instantiated to a type whose members support the overloaded operations associated withT.

Type classes were first implemented in theHaskell programming languageafter first being proposed byPhilip Wadlerand Stephen Blott as an extension to "eqtypes" inStandard ML,[1][2]and were originally conceived as a way of implementingoverloaded arithmetic and equality operatorsin a principled fashion.[3][2] 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 thecompiler frontendor the underlying type system.[4]

Overview[edit]

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 classEqintended to contain types that admit equality would be declared in the following way:

classEqawhere
(==)::a->a->Bool
(/=)::a->a->Bool

whereais one instance of the type classEq,andadefines the function signatures for 2 functions (the equality and inequality functions), which each take 2 arguments of typeaand return a boolean.

The type variableahaskind(is also known asTypein the latestGHCrelease),[5]meaning that the kind ofEqis

Eq::Type->Constraint

The declaration may be read as stating a "typeabelongs to type classEqif there are functions named(==),and(/=),of the appropriate types, defined on it ". A programmer could then define a functionelem(which determines if an element is in a list) in the following way:

elem::Eqa=>a->[a]->Bool
elemy[]=False
elemy(x:xs)=(x==y)||elemyxs

The functionelemhas the typea -> [a] -> Boolwith the contextEq a,which constrains the types whichacan range over to thoseawhich belong to theEqtype class. (Note:Haskell=>can be called a 'class constraint'.)

A programmer can make any typeta member of a given type classCby using aninstance declarationthat defines implementations of all ofC's methods for the particular typet.For instance, if a programmer defines a new data typet,they may then make this new type an instance ofEqby providing an equality function over values of typetin whatever way they see fit. Once they have done this, they may use the functionelemon[t],that is, lists of elements of typet.

Note that type classes are different fromclassesin object-oriented programming languages. In particular,Eqis not a type: there is no such thing as avalueof typeEq.

Type classes are closely related to parametric polymorphism. For example, note that the type ofelemas specified above would be the parametrically polymorphic typea -> [a] -> Boolwere it not for the type class constraint "Eq a =>".

Higher-kinded polymorphism[edit]

A type class need not take a type variable ofkindTypebut 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 asMaybe,rather than data constructors such asJust). An example is theMonadclass:

classMonadmwhere
return::a->ma
(>>=)::ma->(a->mb)->mb

The fact that m is applied to a type variable indicates that it has kindType -> Type,i.e. it takes a type and returns a type, the kind ofMonadis thus:

Monad::(Type->Type)->Constraint

Multi-parameter type classes[edit]

Type classes permit multiple type parameters, and so type classes can be seen as relations on types.[6]For example, in theGHCstandard library, the classIArrayexpresses a general immutable array interface. In this class, the type class constraintIArray a emeans thatais an array type that contains elements of typee.(This restriction on polymorphism is used to implementunboxedarray types, for example.)

Likemultimethods[citation needed],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;[7]: minute 25:12 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 andHugs,support multi-parameter type classes.

Functional dependencies[edit]

In Haskell, type classes have been refined to allow the programmer to declare functional dependencies between type parameters—a conceptinspired from relational database theory.[8][9]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 generalmonadmwhich carries a state parameter of typessatisfies the type class constraintMonad.State s m.In this constraint, there is a functional dependencym -> s.This means that for a given monadmof type classMonad.State,the state type accessible frommis uniquely determined. This aids the compiler intype inference,as well as aiding the programmer intype-directed programming.

Simon Peyton-Joneshas objected to the introduction of functional dependencies in Haskell on grounds of complexity.[10]

Type classes and implicit parameters[edit]

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::Numa=>[a]->a

can be intuitively treated as a function that implicitly accepts an instance ofNum:

sum_::Num_a->[a]->a

The instanceNum_ ais essentially a record that contains the instance definition ofNum 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 moreflexible– you can pass different instances ofNum Int.In contrast, type classes enforce the so-calledcoherenceproperty, 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.[11]

As an example, an orderedset(of typeSet a) requires atotal orderingon the elements (of typea) in order to function. This can be evidenced by a constraintOrd 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 ofOrd ato functions that operate on the set may lead to incorrect results (or crashes). Thus, enforcing coherence ofOrd ain this particular scenario is crucial.

Instances (or "dictionaries" ) inScalatype classes are just ordinary values in the language, rather than a completely separate kind of entity.[12][13]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[14]documentation:

// A type class to provide textual representation
traitShow[A]{
defshow(f:A):String
}

// A polymorphic function that works only when there is an implicit
// instance of Show[A] available
deflog[A](a:A)(implicits:Show[A])=println(s.show(a))

// An instance for String
implicitvalstringShow=newShow[String]{
defshow(s:String)=s
}

// The parameter stringShow was inserted by the compiler.
scala>log("a string")
astring

Coq(version 8.2 onwards) also supports type classes by inferring the appropriate instances.[15]Recent versions ofAgda2 also provide a similar feature, called "instance arguments".[16]

Other approaches to operator overloading[edit]

InStandard ML,the mechanism of "equality types" corresponds roughly to Haskell's built-in type classEq,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 andOCaml'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 forad hocpolymorphism.[17] The object oriented subset ofOCamlis yet another approach which is somewhat comparable to the one of type classes.

Related notions[edit]

An analogous notion for overloaded data (implemented inGHC) is that oftype family.[18]

InC++notably C++20 has support for type classes using theConcepts (C++).As an illustration, the above mentioned Haskell example of typeclass Eq would be written as

template<typenameT>
conceptEqual=
requires(Ta,Tb){
{a==b}->std::convertible_to<bool>;
{a!=b}->std::convertible_to<bool>;
};

InCleantypeclasses are similar to Haskell, but have a slightly different syntax.

Rustsupportstraits,which are a limited form of type classes with coherence.[19]

Mercuryhas typeclasses, although they are not exactly the same as in Haskell.[further explanation needed]

InScala,type classes are aprogramming idiomwhich 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 assistantCoqhas 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[edit]

References[edit]

  1. ^Morris, John G. (2013).Type Classes and Instance Chains: A Relational Approach(PDF)(PhD). Department of Computer Science, Portland State University.doi:10.15760/etd.1010.
  2. ^abWadler, P.; Blott, S. (1989)."How to make ad-hoc polymorphism less ad hoc".Proceedings of the 16th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (POPL '89).Association for Computing Machinery. pp. 60–76.doi:10.1145/75277.75283.ISBN0897912942.S2CID15327197.
  3. ^Kaes, Stefan (March 1988). "Parametric overloading in polymorphic programming languages".Proc. 2nd European Symposium on Programming Languages.doi:10.1007/3-540-19027-9_9.
  4. ^Appel, A.W.; MacQueen, D.B. (1991). "Standard ML of New Jersey". In Maluszyński, J.; Wirsing, M. (eds.).Programming Language Implementation and Logic Programming. PLILP 1991.Lecture Notes in Computer Science. Vol. 528. Springer. pp. 1–13.CiteSeerX10.1.1.55.9444.doi:10.1007/3-540-54444-5_83.ISBN3-540-54444-5.
  5. ^TypefromData.Kindappeared in version 8 of theGlasgow Haskell Compiler
  6. ^Haskell'pageMultiParamTypeClasses.
  7. ^In GHC, the C Core uses Girard & Reynold's System F type signatures to identify a typed case for processing in the optimization phases. -- Simon Peyton-Jones "Into the Core - Squeezing Haskell into Nine Constructors "Erlang User Conference, Sep 14, 2016
  8. ^Jones, Mark P. (2000)."Type Classes with Functional Dependencies".In Smolka, G. (ed.).Programming Languages and Systems. ESOP 2000.Lecture Notes in Computer Science. Vol. 1782. Springer. pp. 230–244.CiteSeerX10.1.1.26.7153.doi:10.1007/3-540-46425-5_15.ISBN3-540-46425-5.
  9. ^Haskell'pageFunctionalDependencies.
  10. ^Peyton-Jones, Simon (2006)."MPTCs and functional dependencies".Haskell-prime mailing list.
  11. ^Kmett, Edward.Type Classes vs. the World.Boston Haskell Meetup.Archivedfrom the original on 2021-12-21.
  12. ^Oliveira, Bruno C.d.S.; Moors, Adriaan; Odersky, Martin (2010)."Type Classes as Objects and Implicits"(PDF).Proceedings of the ACM International Conference on Object Oriented Programming Systems Languages and Applications (OOPSLA '10).Association for Computing Machinery. pp. 341–360.CiteSeerX10.1.1.205.2737.doi:10.1145/1869459.1869489.ISBN9781450302036.S2CID207183083.
  13. ^"The Neophyte's Guide to Scala Part 12: Type classes - Daniel Westheide".
  14. ^typelevel.org, Scala Cats
  15. ^Castéran, P.; Sozeau, M. (2014)."A Gentle Introduction to Type Classes and Relations in Coq"(PDF).CiteSeerX10.1.1.422.8091.
  16. ^"Modelling Type Classes With Instance Arguments".
  17. ^Dreyer, Derek; Harper, Robert; Chakravarty, Manuel M.T. (2007). "Modular Type Classes".Proceedings of the 34th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (POPL '07).pp. 63–70. See p. 63.doi:10.1145/1190216.1190229.ISBN978-1595935755.S2CID1828213.TR-2006-03.
  18. ^"GHC/Type families - HaskellWiki".
  19. ^Turon, Aaron (2017)."Specialization, coherence, and API evolution".

External links[edit]