Intersection type

Last updated

In type theory, an intersection type can be allocated to values that can be assigned both the type and the type . This value can be given the intersection type in an intersection type system. [1] Generally, if the ranges of values of two types overlap, then a value belonging to the intersection of the two ranges can be assigned the intersection type of these two types. Such a value can be safely passed as argument to functions expecting either of the two types. For example, in Java the class Boolean implements both the Serializable and the Comparable interfaces. Therefore, an object of type Boolean can be safely passed to functions expecting an argument of type Serializable and to functions expecting an argument of type Comparable.

Contents

Intersection types are composite data types. Similar to product types, they are used to assign several types to an object. However, product types are assigned to tuples, so that each tuple element is assigned a particular product type component. In comparison, underlying objects of intersection types are not necessarily composite. A restricted form of intersection types are refinement types.

Intersection types are useful for describing overloaded functions. [2] For example, if number=>number is the type of function taking a number as an argument and returning a number, and string=>string is the type of function taking a string as an argument and returning a string, then the intersection of these two types can be used to describe (overloaded) functions that do one or the other, based on what type of input they are given.

Contemporary programming languages, including Ceylon, Flow, Java, Scala, TypeScript, and Whiley (see comparison of languages with intersection types), use intersection types to combine interface specifications and to express ad hoc polymorphism. Complementing parametric polymorphism, intersection types may be used to avoid class hierarchy pollution from cross-cutting concerns and reduce boilerplate code, as shown in the TypeScript example below.

The type theoretic study of intersection types is referred to as the intersection type discipline. [3] Remarkably, program termination can be precisely characterized using intersection types. [4]

TypeScript example

TypeScript supports intersection types, [5] improving expressiveness of the type system and reducing potential class hierarchy size, demonstrated as follows.

The following program code defines the classes Chicken, Cow, and RandomNumberGenerator that each have a method produce returning an object of either type Egg, Milk, or number. Additionally, the functions eatEgg and drinkMilk require arguments of type Egg and Milk, respectively.

classEgg{privatekind:"Egg"}classMilk{privatekind:"Milk"}// produces eggsclassChicken{produce(){returnnewEgg();}}// produces milkclassCow{produce(){returnnewMilk();}}// produces a random numberclassRandomNumberGenerator{produce(){returnMath.random();}}// requires an eggfunctioneatEgg(egg:Egg){return"I ate an egg.";}// requires milkfunctiondrinkMilk(milk:Milk){return"I drank some milk.";}

The following program code defines the ad hoc polymorphic function animalToFood that invokes the member function produce of the given object animal. The function animalToFood has two type annotations, namely ((_:Chicken)=>Egg) and ((_:Cow)=>Milk), connected via the intersection type constructor &. Specifically, animalToFood when applied to an argument of type Chicken returns an object of type type Egg, and when applied to an argument of type Cow returns an object of type type Milk. Ideally, animalToFood should not be applicable to any object having (possibly by chance) a produce method.

// given a chicken, produces an egg; given a cow, produces milkletanimalToFood:((_:Chicken)=>Egg)&((_:Cow)=>Milk)=function(animal:any){returnanimal.produce();};

Finally, the following program code demonstrates type safe use of the above definitions.

varchicken=newChicken();varcow=newCow();varrandomNumberGenerator=newRandomNumberGenerator();console.log(chicken.produce());// Egg { }console.log(cow.produce());// Milk { }console.log(randomNumberGenerator.produce());//0.2626353555444987console.log(animalToFood(chicken));// Egg { }console.log(animalToFood(cow));// Milk { }//console.log(animalToFood(randomNumberGenerator)); // ERROR: Argument of type 'RandomNumberGenerator' is not assignable to parameter of type 'Cow'console.log(eatEgg(animalToFood(chicken)));// I ate an egg.//console.log(eatEgg(animalToFood(cow))); // ERROR: Argument of type 'Milk' is not assignable to parameter of type 'Egg'console.log(drinkMilk(animalToFood(cow)));// I drank some milk.//console.log(drinkMilk(animalToFood(chicken))); // ERROR: Argument of type 'Egg' is not assignable to parameter of type 'Milk'

The above program code has the following properties:

Comparison to inheritance

The above minimalist example can be realized using inheritance, for instance by deriving the classes Chicken and Cow from a base class Animal. However, in a larger setting, this could be disadvantageous. Introducing new classes into a class hierarchy is not necessarily justified for cross-cutting concerns, or maybe outright impossible, for example when using an external library. Imaginably, the above example could be extended with the following classes:

This may require additional classes (or interfaces) specifying whether a produce method is available, whether the produce method returns food, and whether the produce method can be used repeatedly. Overall, this may pollute the class hierarchy.

Comparison to duck typing

The above minimalist example already shows that duck typing is less suited to realize the given scenario. While the class RandomNumberGenerator contains a produce method, the object randomNumberGenerator should not be a valid argument for animalToFood. The above example can be realized using duck typing, for instance by introducing a new field argumentForAnimalToFood to the classes Chicken and Cow signifying that objects of corresponding type are valid arguments for animalToFood. However, this would not only increase the size of the respective classes (especially with the introduction of more methods similar to animalToFood), but is also a non-local approach with respect to animalToFood.

Comparison to function overloading

The above example can be realized using function overloading, for instance by implementing two methods animalToFood(animal:Chicken):Egg and animalToFood(animal:Cow):Milk. In TypeScript, such a solution is almost identical to the provided example. Other programming languages, such as Java, require distinct implementations of the overloaded method. This may lead to either code duplication or boilerplate code.

Comparison to the visitor pattern

The above example can be realized using the visitor pattern. It would require each animal class to implement an accept method accepting an object implementing the interface AnimalVisitor (adding non-local boilerplate code). The function animalToFood would be realized as the visit method of an implementation of AnimalVisitor. Unfortunately, the connection between the input type (Chicken or Cow) and the result type (Egg or Milk) would be difficult to represent.

Limitations

On the one hand, intersection types can be used to locally annotate different types to a function without introducing new classes (or interfaces) to the class hierarchy. On the other hand, this approach requires all possible argument types and result types to be specified explicitly. If the behavior of a function can be specified precisely by either a unified interface, parametric polymorphism, or duck typing, then the verbose nature of intersection types is unfavorable. Therefore, intersection types should be considered complementary to existing specification methods.

Dependent intersection type

A dependent intersection type, denoted , is a dependent type in which the type may depend on the term variable . [6] In particular, if a term has the dependent intersection type , then the term has both the type and the type , where is the type which results from replacing all occurrences of the term variable in by the term .

Scala example

Scala supports type declarations [7] as object members. This allows a type of an object member to depend on the value of another member, which is called a path-dependent type. [8] For example, the following program text defines a Scala trait Witness, which can be used to implement the singleton pattern. [9]

traitWitness{typeTvalvalue:T{}}

The above trait Witness declares the member T, which can be assigned a type as its value, and the member value, which can be assigned a value of type T. The following program text defines an object booleanWitness as instance of the above trait Witness. The object booleanWitness defines the type T as Boolean and the value value as true. For example, executing System.out.println(booleanWitness.value) prints true on the console.

objectbooleanWitnessextendsWitness{typeT=Booleanvalvalue=true}

Let be the type (specifically, a record type) of objects having the member of type . In the above example, the object booleanWitness can be assigned the dependent intersection type . The reasoning is as follows. The object booleanWitness has the member T that is assigned the type Boolean as its value. Since Boolean is a type, the object booleanWitness has the type . Additionally, the object booleanWitness has the member value that is assigned the value true of type Boolean. Since the value of booleanWitness.T is Boolean, the object booleanWitness has the type . Overall, the object booleanWitness has the intersection type . Therefore, presenting self-reference as dependency, the object booleanWitness has the dependent intersection type .

Alternatively, the above minimalistic example can be described using dependent record types. [10] In comparison to dependent intersection types, dependent record types constitute a strictly more specialized type theoretic concept. [6]

Intersection of a type family

An intersection of a type family, denoted , is a dependent type in which the type may depend on the term variable . In particular, if a term has the type , then for each term of type , the term has the type . This notion is also called implicit Pi type , [11] observing that the argument is not kept at term level.

Comparison of languages with intersection types

LanguageActively developedParadigm(s)StatusFeatures
C# Yes [12] Under discussion [13] Additionally, generic type parameters can have constraints that require their (monomorphized) type-arguments to implement multiple interfaces, whereupon the runtime type represented by the generic type parameter becomes an intersection-type of all listed interfaces.
Ceylon No [14] Supported [15]
  • Type refinement
  • Interface composition
  • Subtyping in width
F# Yes [16] Under discussion [17] ?
FlowYes [18] Supported [19]
  • Type refinement
  • Interface composition
Forsythe NoSupported [20]
  • Function type intersection
  • Distributive, co- and contravariant function type subtyping
Java Yes [21] Supported [22]
  • Type refinement
  • Interface composition
  • Subtyping in width
PHP Yes [23] Supported [24]
  • Type refinement
  • Interface composition
Scala Yes [25] Supported [26] [27]
  • Type refinement
  • Trait composition
  • Subtyping in width
TypeScript Yes [28] Supported [5]
  • Arbitrary type intersection
  • Interface composition
  • Subtyping in width and depth
Whiley Yes [29] Supported [30] ?

Related Research Articles

<span class="mw-page-title-main">Inner product space</span> Generalization of the dot product; used to define Hilbert spaces

In mathematics, an inner product space is a real vector space or a complex vector space with an operation called an inner product. The inner product of two vectors in the space is a scalar, often denoted with angle brackets such as in . Inner products allow formal definitions of intuitive geometric notions, such as lengths, angles, and orthogonality of vectors. Inner product spaces generalize Euclidean vector spaces, in which the inner product is the dot product or scalar product of Cartesian coordinates. Inner product spaces of infinite dimension are widely used in functional analysis. Inner product spaces over the field of complex numbers are sometimes referred to as unitary spaces. The first usage of the concept of a vector space with an inner product is due to Giuseppe Peano, in 1898.

<span class="mw-page-title-main">Uncertainty principle</span> Foundational principle in quantum physics

The uncertainty principle, also known as Heisenberg's indeterminacy principle, is a fundamental concept in quantum mechanics. It states that there is a limit to the precision with which certain pairs of physical properties, such as position and momentum, can be simultaneously known. In other words, the more accurately one property is measured, the less accurately the other property can be known.

<span class="mw-page-title-main">Allan variance</span> Measure of frequency stability in clocks and oscillators

The Allan variance (AVAR), also known as two-sample variance, is a measure of frequency stability in clocks, oscillators and amplifiers. It is named after David W. Allan and expressed mathematically as . The Allan deviation (ADEV), also known as sigma-tau, is the square root of the Allan variance, .

<span class="mw-page-title-main">Data type</span> Attribute of data

In computer science and computer programming, a data type is a collection or grouping of data values, usually specified by a set of possible values, a set of allowed operations on these values, and/or a representation of these values as machine types. A data type specification in a program constrains the possible values that an expression, such as a variable or a function call, might take. On literal data, it tells the compiler or interpreter how the programmer intends to use the data. Most programming languages support basic data types of integer numbers, floating-point numbers, characters and Booleans.

In physics, a Langevin equation is a stochastic differential equation describing how a system evolves when subjected to a combination of deterministic and fluctuating ("random") forces. The dependent variables in a Langevin equation typically are collective (macroscopic) variables changing only slowly in comparison to the other (microscopic) variables of the system. The fast (microscopic) variables are responsible for the stochastic nature of the Langevin equation. One application is to Brownian motion, which models the fluctuating motion of a small particle in a fluid.

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. Usually the terms are various language constructs of a computer program, such as variables, expressions, functions, or modules. A type system dictates the operations that can be performed on a term. For variables, the type system determines the allowed values of that term. Type systems formalize and enforce the otherwise implicit categories the programmer uses for algebraic data types, data structures, or other components.

In mathematics, the covariant derivative is a way of specifying a derivative along tangent vectors of a manifold. Alternatively, the covariant derivative is a way of introducing and working with a connection on a manifold by means of a differential operator, to be contrasted with the approach given by a principal connection on the frame bundle – see affine connection. In the special case of a manifold isometrically embedded into a higher-dimensional Euclidean space, the covariant derivative can be viewed as the orthogonal projection of the Euclidean directional derivative onto the manifold's tangent space. In this case the Euclidean derivative is broken into two parts, the extrinsic normal component and the intrinsic covariant derivative component.

<span class="mw-page-title-main">Drude model</span> Model of electrical conduction

The Drude model of electrical conduction was proposed in 1900 by Paul Drude to explain the transport properties of electrons in materials. Basically, Ohm's law was well established and stated that the current J and voltage V driving the current are related to the resistance R of the material. The inverse of the resistance is known as the conductance. When we consider a metal of unit length and unit cross sectional area, the conductance is known as the conductivity, which is the inverse of resistivity. The Drude model attempts to explain the resistivity of a conductor in terms of the scattering of electrons by the relatively immobile ions in the metal that act like obstructions to the flow of electrons.

In computational complexity theory, the polynomial hierarchy is a hierarchy of complexity classes that generalize the classes NP and co-NP. Each class in the hierarchy is contained within PSPACE. The hierarchy can be defined using oracle machines or alternating Turing machines. It is a resource-bounded counterpart to the arithmetical hierarchy and analytical hierarchy from mathematical logic. The union of the classes in the hierarchy is denoted PH.

<span class="mw-page-title-main">Lawson criterion</span> Criterion for igniting a nuclear fusion chain reaction

The Lawson criterion is a figure of merit used in nuclear fusion research. It compares the rate of energy being generated by fusion reactions within the fusion fuel to the rate of energy losses to the environment. When the rate of production is higher than the rate of loss, the system will produce net energy. If enough of that energy is captured by the fuel, the system will become self-sustaining and is said to be ignited.

In probability theory and statistics, given a stochastic process, the autocovariance is a function that gives the covariance of the process with itself at pairs of time points. Autocovariance is closely related to the autocorrelation of the process in question.

<span class="mw-page-title-main">Correlation function (statistical mechanics)</span> Measure of a systems order

In statistical mechanics, the correlation function is a measure of the order in a system, as characterized by a mathematical correlation function. Correlation functions describe how microscopic variables, such as spin and density, at different positions are related. More specifically, correlation functions quantify how microscopic variables co-vary with one another on average across space and time. A classic example of such spatial correlations is in ferro- and antiferromagnetic materials, where the spins prefer to align parallel and antiparallel with their nearest neighbors, respectively. The spatial correlation between spins in such materials is shown in the figure to the right.

In functional and convex analysis, and related disciplines of mathematics, the polar set is a special convex set associated to any subset of a vector space lying in the dual space The bipolar of a subset is the polar of but lies in .

<span class="mw-page-title-main">KMS state</span> Type of state in thermal systems

In the statistical mechanics of quantum mechanical systems and quantum field theory, the properties of a system in thermal equilibrium can be described by a mathematical object called a Kubo–Martin–Schwinger (KMS) state: a state satisfying the KMS condition.

In mathematics, a metric connection is a connection in a vector bundle E equipped with a bundle metric; that is, a metric for which the inner product of any two vectors will remain the same when those vectors are parallel transported along any curve. This is equivalent to:

Resonance fluorescence is the process in which a two-level atom system interacts with the quantum electromagnetic field if the field is driven at a frequency near to the natural frequency of the atom.

<span class="mw-page-title-main">Wrapped normal distribution</span>

In probability theory and directional statistics, a wrapped normal distribution is a wrapped probability distribution that results from the "wrapping" of the normal distribution around the unit circle. It finds application in the theory of Brownian motion and is a solution to the heat equation for periodic boundary conditions. It is closely approximated by the von Mises distribution, which, due to its mathematical simplicity and tractability, is the most commonly used distribution in directional statistics.

<span class="mw-page-title-main">Modified Allan variance</span>

The modified Allan variance (MVAR), also known as mod σy2(τ), is a variable bandwidth modified variant of Allan variance, a measurement of frequency stability in clocks, oscillators and amplifiers. Its main advantage relative to Allan variance is its ability to separate white phase noise from flicker phase noise.

In statistical mechanics, the Griffiths inequality, sometimes also called Griffiths–Kelly–Sherman inequality or GKS inequality, named after Robert B. Griffiths, is a correlation inequality for ferromagnetic spin systems. Informally, it says that in ferromagnetic spin systems, if the 'a-priori distribution' of the spin is invariant under spin flipping, the correlation of any monomial of the spins is non-negative; and the two point correlation of two monomial of the spins is non-negative.

A Hindley–Milner (HM) type system is a classical type system for the lambda calculus with parametric polymorphism. It is also known as Damas–Milner or Damas–Hindley–Milner. It was first described by J. Roger Hindley and later rediscovered by Robin Milner. Luis Damas contributed a close formal analysis and proof of the method in his PhD thesis.

References

  1. Barendregt, Henk; Coppo, Mario; Dezani-Ciancaglini, Mariangiola (1983). "A filter lambda model and the completeness of type assignment". Journal of Symbolic Logic. 48 (4): 931–940. doi:10.2307/2273659. JSTOR   2273659. S2CID   45660117.
  2. Palsberg, Jens (2012). "Overloading is NP-Complete". Logic and Program Semantics. Lecture Notes in Computer Science. Vol. 7230. pp. 204–218. doi:10.1007/978-3-642-29485-3_13. ISBN   978-3-642-29484-6.
  3. Henk Barendregt; Wil Dekkers; Richard Statman (20 June 2013). Lambda Calculus with Types. Cambridge University Press. pp. 1–. ISBN   978-0-521-76614-2.
  4. Ghilezan, Silvia (1996). "Strong normalization and typability with intersection types". Notre Dame Journal of Formal Logic. 37 (1): 44–52. doi: 10.1305/ndjfl/1040067315 .
  5. 1 2 "Intersection Types in TypeScript" . Retrieved 2019-08-01.
  6. 1 2 Kopylov, Alexei (2003). "Dependent intersection: A new way of defining records in type theory". 18th IEEE Symposium on Logic in Computer Science. LICS 2003. IEEE Computer Society. pp. 86–95. CiteSeerX   10.1.1.89.4223 . doi:10.1109/LICS.2003.1210048.
  7. "Type declarations in Scala" . Retrieved 2019-08-15.
  8. Amin, Nada; Grütter, Samuel; Odersky, Martin; Rompf, Tiark; Stucki, Sandro (2016). "The Essence of Dependent Object Types". A List of Successes That Can Change the World (PDF). Lecture Notes in Computer Science. Vol. 9600. Springer. pp. 249–272. doi:10.1007/978-3-319-30936-1_14. ISBN   978-3-319-30935-4.
  9. "Singletons in the Scala shapeless library". GitHub . Retrieved 2019-08-15.
  10. Pollack, Robert (2000). "Dependently typed records for representing mathematical structure". Theorem Proving in Higher Order Logics, 13th International Conference. TPHOLs 2000. Springer. pp. 462–479. doi:10.1007/3-540-44659-1_29.
  11. Stump, Aaron (2018). "From realizability to induction via dependent intersection". Annals of Pure and Applied Logic. 169 (7): 637–655. doi: 10.1016/j.apal.2018.03.002 .
  12. "C# Guide" . Retrieved 2019-08-08.
  13. "Discussion: Union and Intersection types in C Sharp". GitHub . Retrieved 2019-08-08.
  14. "Eclipse Ceylon™". 19 July 2017. Retrieved 2023-08-16.
  15. "Intersection Types in Ceylon". 19 July 2017. Retrieved 2019-08-08.
  16. "F# Software Foundation" . Retrieved 2019-08-08.
  17. "Add Intersection Types to F Sharp". GitHub . Retrieved 2019-08-08.
  18. "Flow: A Static Type Checker for JavaScript" . Retrieved 2019-08-08.
  19. "Intersection Type Syntax in Flow" . Retrieved 2019-08-08.
  20. Reynolds, J. C. (1988). Preliminary design of the programming language Forsythe.
  21. "Java Software" . Retrieved 2019-08-08.
  22. "IntersectionType (Java SE 12 & JDK 12)" . Retrieved 2019-08-01.
  23. "php.net".
  24. "PHP.Watch - PHP 8.1: Intersection Types".
  25. "The Scala Programming Language" . Retrieved 2019-08-08.
  26. "Compound Types in Scala" . Retrieved 2019-08-01.
  27. "Intersection Types in Dotty" . Retrieved 2019-08-01.
  28. "TypeScript - JavaScript that scales" . Retrieved 2019-08-01.
  29. "Whiley: an Open Source Programming Language with Extended Static Checking" . Retrieved 2019-08-01.
  30. "Whiley language specification" (PDF). Retrieved 2019-08-01.