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
C (since
C23),
C++ (since
C++11),
C# (starting with version 3.0),
Chapel,
Clean,
Crystal,
D,
Dart,
F#,
FreeBASIC,
Go,
Haskell,
Java (starting with version 10),
Julia,
Kotlin,
ML,
Nim,
OCaml,
Opa, Q#,
RPython,
Rust,
Scala,
Swift,
TypeScript,
Vala,
Zig, and
Visual Basic (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, int add_one(int x), declares that add_one is a function that takes one argument, an
integer, and returns an integer. int result; declares that the local variable result is an integer. In a hypothetical language supporting type inference, the code might be written like this instead: 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 result and x have type integer since the constant 1 is type integer, and hence that add_one is a function int -> int. The variable result2 isn't used in a legal manner, so it wouldn't have a type. In the imaginary language in which the last example is written, the compiler would assume that, in the absence of information to the contrary, + takes two integers and returns one integer. (This is how it works in, for example,
OCaml.) From this, the type inferencer can infer that the type of x + 1 is an integer, which means result is an integer and thus the return value of add_one is an integer. Similarly, since + requires both of its arguments be of the same type, x must be an integer, and thus, add_one accepts one integer as an argument. However, in the subsequent line,
result2 is calculated by adding a decimal 1.0 with
floating-point arithmetic, causing a conflict in the use of x for both integer and floating-point expressions. The correct type-inference algorithm for such a situation has been known
since 1958 and has been known to be correct since 1982. It revisits the prior inferences and uses the most general type from the outset: in this case floating-point. This can however have detrimental implications, for instance using a floating-point from the outset can introduce precision issues that would have not been there with an integer type. 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 x to a floating point. This can be correct if the calling contexts never supply a floating point argument. Such a situation shows the difference between
type inference, which does not involve
type conversion, and
implicit type conversion, which forces data to a different data type, often without restrictions. 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: :A version that accepts an integer input and uses implicit type conversion. :A version that accepts a floating-point number as input and uses floating point instructions throughout. ==Technical description==