Type inference
Type inference, sometimes called type reconstruction,[1]: 320 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 linguistics. Nontechnical explanationIn a typed language, a term's type determines the ways it can and cannot be used in that language. For example, consider the English language and terms that could fill in the blank in the phrase "sing _." The term "a song" is of singable type, so it could be placed in the blank to form a meaningful phrase: "sing a song." On the other hand, the term "a friend" does not have the singable type, so "sing a friend" is nonsense. At best it might be metaphor; bending type rules is a feature of poetic language. A term's type can also affect the interpretation of operations involving that term. For instance, "a song" is of composable type, so we interpret it as the thing created in the phrase "write a song". On the other hand, "a friend" is of recipient type, so we interpret it as the addressee in the phrase "write a friend". In normal language, we would be surprised if "write a song" meant addressing a letter to a song or "write a friend" meant drafting a friend on paper. Terms with different types can even refer to materially the same thing. For example, we would interpret "to hang up the clothes line" as putting it into use, but "to hang up the leash" as putting it away, even though, in context, both "clothes line" and "leash" might refer the same rope, just at different times. Typings are often used to prevent an object from being considered too generally. For instance, if the type system treats all numbers as the same, then a programmer who accidentally writes code where There are several ways that a term can get its type:
Especially in programming languages, there may not be much shared background knowledge available to the computer. In manifestly typed languages, this means that most types have to be declared explicitly. Type inference aims to alleviate this burden, freeing the author from declaring types that the computer should be able to deduce from context. Type-checking vs. type-inferenceIn a typing, an expression E is opposed to a type T, formally written as E : T. Usually a typing only makes sense within some context, which is omitted here. In this setting, the following questions are of particular interest:
For the simply typed lambda calculus, all three questions are decidable. The situation is not as comfortable when more expressive types are allowed. Types in programming languages
Types are a feature present in some strongly statically typed languages. It is often characteristic of functional programming languages in general. Some languages that include type inference include C23,[2] C++11,[3] C# (starting with version 3.0), Chapel, Clean, Crystal, D, F#,[4] FreeBASIC, Go, Haskell, Java (starting with version 10), Julia,[5] Kotlin,[6] ML, Nim, OCaml, Opa, Q#, RPython, Rust,[7] Scala,[8] Swift,[9] TypeScript,[10] Vala,[11] Dart,[12] and Visual Basic[13] (starting with version 9.0). The majority of them use a simple form of type inference; the Hindley–Milner type system can provide more complete type inference. The ability to infer types automatically makes many programming tasks easier, leaving the programmer free to omit type annotations while still permitting type checking. In some programming languages, all values have a data type explicitly declared at compile time, limiting the values a particular expression can take on at run-time. Increasingly, just-in-time compilation blurs the distinction between run time and compile time. However, historically, if the type of a value is known only at run-time, these languages are dynamically typed. In other languages, the type of an expression is known only at compile time; these languages are statically typed. In most statically typed languages, the input and output types of functions and local variables ordinarily must be explicitly provided by type annotations. For example, in ANSI C: int add_one(int x) {
int result; /* declare integer result */
result = x + 1;
return result;
}
The signature of this function definition, add_one(x) {
var result; /* inferred-type variable result */
var result2; /* inferred-type variable result #2 */
result = x + 1;
result2 = x + 1.0; /* this line won't work (in the proposed language) */
return result;
}
This is identical to how code is written in the language Dart, except that it is subject to some added constraints as described below. It would be possible to infer the types of all the variables at compile time. In the example above, the compiler would infer that In the imaginary language in which the last example is written, the compiler would assume that, in the absence of information to the contrary, However, in the subsequent line, result2 is calculated by adding a decimal Frequently, however, degenerate type-inference algorithms are used that cannot backtrack and instead generate an error message in such a situation. This behavior may be preferable as type inference may not always be neutral algorithmically, as illustrated by the prior floating-point precision issue. An algorithm of intermediate generality implicitly declares result2 as a floating-point variable, and the addition implicitly converts Finally, a significant downside of complex type-inference algorithm is that the resulting type inference resolution is not going to be obvious to humans (notably because of the backtracking), which can be detrimental as code is primarily intended to be comprehensible to humans. The recent emergence of just-in-time compilation allows for hybrid approaches where the type of arguments supplied by the various calling context is known at compile time, and can generate a large number of compiled versions of the same function. Each compiled version can then be optimized for a different set of types. For instance, JIT compilation allows there to be at least two compiled versions of add_one:
Technical descriptionType inference is the ability to automatically deduce, either partially or fully, the type of an expression at compile time. The compiler is often able to infer the type of a variable or the type signature of a function, without explicit type annotations having been given. In many cases, it is possible to omit type annotations from a program completely if the type inference system is robust enough, or the program or language is simple enough. To obtain the information required to infer the type of an expression, the compiler either gathers this information as an aggregate and subsequent reduction of the type annotations given for its subexpressions, or through an implicit understanding of the type of various atomic values (e.g. true : Bool; 42 : Integer; 3.14159 : Real; etc.). It is through recognition of the eventual reduction of expressions to implicitly typed atomic values that the compiler for a type inferring language is able to compile a program completely without type annotations. In complex forms of higher-order programming and polymorphism, it is not always possible for the compiler to infer as much, and type annotations are occasionally necessary for disambiguation. For instance, type inference with polymorphic recursion is known to be undecidable. Furthermore, explicit type annotations can be used to optimize code by forcing the compiler to use a more specific (faster/smaller) type than it had inferred.[14] Some methods for type inference are based on constraint satisfaction[15] or satisfiability modulo theories.[16] High-Level ExampleAs an example, the Haskell function map f [] = []
map f (first:rest) = f first : map f rest
(Recall that Type inference on the Putting the parts together leads to map :: (a -> b) -> [a] -> [b]
It turns out that this is also the most general type, since no further constraints apply. As the inferred type of Detailed ExampleThe algorithms used by programs like compilers are equivalent to the informally structured reasoning above, but a bit more verbose and methodical. The exact details depend on the inference algorithm chosen (see the following section for the best-known algorithm), but the example below gives the general idea. We again begin with the definition of map f [] = []
map f (first:rest) = f first : map f rest
(Again, remember that the First, we make fresh type variables for each individual term:
Then we make fresh type variables for subexpressions built from these terms, constraining the type of the function being invoked accordingly:
We also constrain the left and right sides of each equation to unify with each other: α ~ β -> [γ] -> κ ζ -> [ζ] -> [ζ] ~ η -> θ -> λ α ~ ε -> λ -> μ ε ~ η -> ν α ~ ε -> θ -> ξ ι -> [ι] -> [ι] ~ ν -> ξ -> ο κ ~ [δ] μ ~ ο Then we substitute until no further variables can be eliminated. The exact order is immaterial; if the code type-checks, any order will lead to the same final form. Let us begin by substituting α ~ β -> [γ] -> [δ] ζ -> [ζ] -> [ζ] ~ η -> θ -> λ α ~ ε -> λ -> ο ε ~ η -> ν α ~ ε -> θ -> ξ ι -> [ι] -> [ι] ~ ν -> ξ -> ο Substituting α ~ β -> [γ] -> [δ] α ~ ε -> [ζ] -> [ι] ε ~ ζ -> ι Substituting α ~ (ζ -> ι) -> [ζ] -> [ι] β -> [γ] -> [δ] ~ (ζ -> ι) -> [ζ] -> [ι] And, finally, substituting α ~ (ζ -> ι) -> [ζ] -> [ι] No more substitutions are possible, and relabeling gives us Hindley–Milner type inference algorithmThe algorithm first used to perform type inference is now informally termed the Hindley–Milner algorithm, although the algorithm should properly be attributed to Damas and Milner.[17] It is also traditionally called type reconstruction.[1]: 320 If a term is well-typed in accordance with Hindley–Milner typing rules, then the rules generate a principal typing for the term. The process of discovering this principal typing is the process of "reconstruction". The origin of this algorithm is the type inference algorithm for the simply typed lambda calculus that was devised by Haskell Curry and Robert Feys in 1958.[citation needed] In 1969 J. Roger Hindley extended this work and proved that their algorithm always inferred the most general type. In 1978 Robin Milner,[18] independently of Hindley's work, provided an equivalent algorithm, Algorithm W. In 1982 Luis Damas[17] finally proved that Milner's algorithm is complete and extended it to support systems with polymorphic references. Side-effects of using the most general typeBy design, type inference will infer the most general type appropriate. However, many languages, especially older programming languages, have slightly unsound type systems, where using a more general types may not always be algorithmically neutral. Typical cases include:
Type inference for natural languagesType inference algorithms have been used to analyze natural languages as well as programming languages.[19][20][21] Type inference algorithms are also used in some grammar induction[22][23] and constraint-based grammar systems for natural languages.[24] References
External links
|
Portal di Ensiklopedia Dunia