How is type inference implemented in a language like C++11 or Go? - algorithm

I saw this question here, but it doesn't answer what I had in mind in particular detail.
If languages like Go or C++11 don't use an inference algorithm like Damas-Milner, what exactly do they do? I don't think it's as simple as taking the type on the right hand side because what if you had something like:
5 + 3.4
How would the compiler decipher what type that is? Is there any algorithm that isn't as simple as
if left is integer and right is float:
return float;
if left is float and right is integer:
return float;
etc... for every possible pattern
And if you could explain things in simple terms that would be great. I'm not studying compiler construction or any of the theoretical topics in great detail, and I don't really speak functional languages or complex mathematical notation.

I don't think it's as simple as taking the type on the right hand side
For basic type inference of the form auto var = some_expression;, it is exactly that simple. Every well-typed expression has exactly one type and that type will be the type of var. There will be no implicit conversion from the type of the expression to another type (as there might be if you gave an explicit type for var).
what if you had something like:
5 + 3.4
The question "What is the type of 5 + 3.4?" isn't specific to type inference, C++ compilers always had to answer this question - even before type inference was introduced.
So let's take a step back and look at how a C++ compiler typechecks the statement some_type var = some_expression;:
First it determines the type of some_expression. So in code you can imagine something like Type exp_type = type_of(exp);. Now it checks whether exp_type is equal to some_type or there exists an implicit conversion from exp_type to some_type. If so, the statement is well-typed and var is introduced into the environment as having the type some_type. Otherwise it is not.
Now when we introduce type inference and write auto var = some_expression;, the equation changes as such: We still do Type exp_type = type_of(exp);, but instead of then comparing it to another type or applying any implicit conversions, we instead simply set exp_type as the type of var.
So now let's get back to 5 + 3.4. What is its type and how does the compiler determine it? In C++ its type is double. The exact rules to determine the type of an arithmetic expression are listed in the C++ standard (look for "usual arithmetic conversions"), but basically boil down to this: Of the two operand types, pick the one that can represent the greater range of values. If the type is smaller than int, convert both operands to int. Otherwise convert both operands to the type you picked.
In code you'd implement this by assigning each numeric type a conversion rank and then doing something like this:
Type type_of_binary_arithmetic_expression(Type lhs_type, Type rhs_type) {
int lhs_rank = conversion_rank(lhs_type);
int rhs_rank = conversion_rank(rhs_type);
if(lhs_rank < INT_RANK && rhs_rank < INT_RANK) return INT_TYPE;
else if(lhs_rank < rhs_rank) return rhs_type;
else return lhs_type;
}
Presumably the rules for Go are somewhat different, but the same principles apply.

Related

Halide::Expr' is not contextually convertible to 'bool' -- Storing values of functions in variables

I am new to using Halide and I am playing around with implementing algorithms first. I am trying to write a function which, depending on the value of the 8 pixels around it, either skips to the next pixel or does some processing and then moves on to the next pixel. When trying to write this I get the following compiler error:
84:5: error: value of type 'Halide::Expr' is not contextually convertible to 'bool'
if(input(x,y) > 0)
I have done all the tutorials and have seen that the select function is an option, but is there a way to either compare the values of a function or store them somewhere?
I also may be thinking about this problem wrong or might not be implementing it with the right "Halide mindset", so any suggestions would be great. Thank you in advance for everything!
The underlying issue here is that, although they are syntactically interleaved, and Halide code is constructed by running C++ code, Halide code is not C++ code and vice versa. Halide code is entirely defined by the Halide::* data structures you build up inside Funcs. if is a C control flow construct; you can use it to conditionally build different Halide programs, but you can't use it inside the logic of the Halide program (inside an Expr/Func). select is to Halide (an Expr which conditionally evaluates to one of two values) as if/else is to C (a statement which conditionally executes one of two sub-statements).
Rest assured, you're hardly alone in having this confusion early on. I want to write a tutorial specifically addressing how to think about staged programming inside Halide.
Until then, the short, "how do I do what I want" answer is as you suspected and as Khouri pointed out: use a select.
Since you've provided no code other than the one line, I'm assuming input is a Func and both x and y are Vars. If so, the result of input(x,y) is an Expr that you cannot evaluate with an if, as the error message indicates.
For the scenario that you describe, you might have something like this:
Var x, y;
Func input; input(x,y) = ...;
Func output; output(x,y) = select
// examine surrounding values
( input(x-1,y-1) > 0
&& input(x+0,y-1) > 0
&& ...
&& input(x+1,y+1) > 0
// true case
, ( input(x-1,y-1)
+ input(x+0,y-1)
+ ...
+ input(x+1,y+1)
) / 8
// false case
, input(x,y)
);
Working in Halide definitely requires a different mindset. You have to think in a more mathematical form. That is, a statement of a(x,y) = b(x,y) will be enforced for all cases of x and y.
Algorithm and scheduling should be separate, although the algorithm may need to be tweaked to allow for better scheduling.

Why literal value has no type?

Type Inference is a powerful attribute of Swift. It means the compiler can infer the literal's type from its value provided by the programmer, the explicit type specification is not needed.
For example var IntNum = 3; the compiler can infer that the variable IntNum is of type Int. In Xcode, If user hits the key and clicks on the variable name, here IntNum, then Xcode tells you what the type it is.
However, if I did that on the literal value 3, Xcode provides nothing. I guess, the literal value I put on the screen simply has no type at all, only the object variable and constant has the type property.
I just guess so, can someone explain that to me?
Cheers
SL
That's right.
From the documentation
Type Safety and Type Inference
Type inference is particularly useful when you declare a constant or
variable with an initial value. This is often done by assigning a
literal value (or literal) to the constant or variable at the point
that you declare it. (A literal value is a value that appears directly
in your source code)
...
If you combine integer and floating-point literals in an expression, a
type of Double will be inferred from the context:
let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
The literal value of 3 has no explicit type in and of itself, and so
an appropriate output type of Double is inferred from the presence of
a floating-point literal as part of the addition.

Why is the behavior of decltype defined the way it is?

From C++ Draft Standard N3337:
7.1.6.2 Simple type specifiers
4 The type denoted by decltype(e) is defined as follows:
— if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
If I understand the above correctly,
int a;
decltype(a) b = 10; // type of b is int
int* ap;
decltype(*ap) c = 10; // Does not work since type of c is int&
Can you explain, or point to some documentation that explains, why decltype(*ap) couldn't have been just int?
The standardization effort of decltype was a herculean effort spanning many years. There were 7 versions of this paper before the committee finally accepted it. The versions were:
N1478 2003-04-28
N1527 2003-09-21
N1607 2004-02-17
N1705 2004-09-12
N1978 2006-04-24
N2115 2006-11-05
N2343 2007-07-18
Remarkably, the seeds of the behavior which you question are in the very first revision: N1478, which introduces the need for "two types of typeof: either to preserve or to drop references in types."
This paper goes on to give a rationale for the reference-preserving variant, including this quote:
On the other hand, the reference-dropping semantics fails to provide a
mechanism for exactly expressing the return types of generic
functions, as demonstrated by Strous- trup [Str02]. This implies that
a reference-dropping typeof would cause problems for writers of
generic libraries.
There is no substitute for reading through these papers. However one might summarize that decltype serves two purposes:
To report the declared type of an identifier.
To report the type of an expression.
For the second use case, recall that expressions are never reference types, but are instead one of lvalues, xvalues, or prvalues. By convention, when decltype is reporting the type of an lvalue expression, it makes the type an lvalue reference, and when the expression is an xvalue, the reported type becomes an rvalue reference.
In your example, *ap is an expression, whereas a is an identifier. So your example makes use of both use cases, as first introduced in N1478.
It is also instructive to note that decltype was not designed in isolation. The rest of the C++ language was evolving during this time period (e.g. rvalue references), and the design of decltype was iterated to keep pace.
Also note that once the decltype proposal was accepted, it continued (and continues to this day) to evolve. See this list of issues:
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_index.html
specifically section 7.1.6.2 (which is the section where the bulk of the decltype specification lives).

Is there a builtin func named "int32"?

The below snippet works fine.
In this case, what "int32" is? A func?
I know there is a type named "int32"
This could be a stupid question. I've just finished A Tour of Go but I could not find the answer.(it's possible I'm missing something.)
package main
import "fmt"
func main() {
var number = int32(5)
fmt.Println(number) //5
}
It is a type conversion, which is required for numeric types.
Conversions are required when different numeric types are mixed in an expression or assignment. For instance, int32 and int are not the same type even though they may have the same size on a particular architecture.
Since you do a variable declaration, you need to specify the type of '5'.
Another option, as mentioned by rightfold in the comments is: var number int32 = 5
(as opposed to a short variable declaration like number := 5)
See also Go FAQ:
The convenience of automatic conversion between numeric types in C is outweighed by the confusion it causes.
When is an expression unsigned? How big is the value? Does it overflow? Is the result portable, independent of the machine on which it executes?
It also complicates the compiler; “the usual arithmetic conversions” are not easy to implement and inconsistent across architectures.
For reasons of portability, we decided to make things clear and straightforward at the cost of some explicit conversions in the code. The definition of constants in Go—arbitrary precision values free of signedness and size annotations—ameliorates matters considerably, though.
A related detail is that, unlike in C, int and int64 are distinct types even if int is a 64-bit type.
The int type is generic; if you care about how many bits an integer holds, Go encourages you to be explicit.

Can we peek out partially inferred typing info. from Ocaml toplevel/compiler for a program that does not compile?

I would like to know, in Ocaml, whether a partial typing info. can be drawn by some existed functionality of toplevel/compiler, for a program that does not compile? Let me explain.
In Ocaml, it's well known that inferred typed can be retrieved by -annot file. However, sometimes we have a piece of code that does not compile due to some typing error. It gives a error exported to the toplevel, of this pattern
"This expression has type A, but was expected type B"
An artificial example would be
# let x =
let y = 5 in
not y;;
Characters 32-33:
not y;;
^
Error: This expression has type int
but an expression was expected of type bool
The programmer of this piece of code should understand well the 2nd part of this message, i.e.,
"y is expected of type bool", because of the "not y" part. However, she/he might have some difficulty to understand the 1st part of this error message: how this "y" is inferred to have type "int"? Thus it would be interesting to have a partial set of inferred types, before the type conflicts are raised. For the example above, one would like the interpreter tells that the first "y" (from "let y = 5") is of type int, by which I will know the reason why the second "y" (from "not y") is infered to be of type int.
Could you tell me whether the described functionality is already provided by some ocaml interpreter/compiler?
In general words, my question is: can ocaml toplevel, or its interpreter, yield partially inferred types that user can retrieve in order to more efficiently find the source of their typing error?
This question might not make sense because of the non-uniqueness of the partially inferred type annotation. However, the example example should show that at least for some cases, and some partially inferred types have its usage.
Thank you for your ideas.
The type annotations by generated by the -annot switch are available even if the program did not compile. You'll see types for the expressions that the compiler got through, and some of them may be incomplete. This doesn't tell you the compiler's reasoning for inferring the types, but it does tell you how far the compiler went and lets you explore what it's inferred.
For example, with this source code:
let x = [(let y = 5 in not y); true];;
x has the type _a list (the compiler hasn't gotten far enough to figure out _a).
y has the type int.
not has the type bool -> bool.
The error message is that the second occurrence of y has the type int (and we've seen where it was inferred) but the context expects the type bool (and we can see that, since not is a function whose argument type is bool).
I don't know how to see these types from the toplevel, but if you have a source file with your code, you can run ocamlc -c -annot, open the source in a suitable editor (such as Emacs) and view the inferred types whether the compilation succeeded or not.

Resources