updated readme
This commit is contained in:
parent
6f4ad20b7d
commit
dd419fc120
@ -37,7 +37,83 @@ Here is the list of files and their purpose:
|
||||
python AST.
|
||||
* `type_def.py`: python class for various types.
|
||||
|
||||
## Type Check Implementation
|
||||
## Variable Scope
|
||||
There is no shadowing in Python, so we decided that variables with the same name
|
||||
in a function must have the same type. For example, the following is not
|
||||
allowed:
|
||||
```python
|
||||
if foo():
|
||||
a = 1
|
||||
else:
|
||||
a = None
|
||||
```
|
||||
|
||||
Also, as variables has to be well typed, they must be initialized before using
|
||||
them. If a variable could be not initialized in some code path, then it is not
|
||||
readable. The following is also not allowed:
|
||||
```python
|
||||
if foo():
|
||||
a = 1
|
||||
a = a + 1
|
||||
```
|
||||
## Generics
|
||||
Generics are supported via type variables.
|
||||
|
||||
* Generic type variable:
|
||||
```python
|
||||
A = TypeVar('A')
|
||||
```
|
||||
* Bounded type variable (`A` can either be `T1` or `T2` or ...):
|
||||
```python
|
||||
A = TypeVar('A', T1, T2, ...)
|
||||
```
|
||||
> Note:
|
||||
>
|
||||
> 1. In normal python, the *bound* of a type variable is actually about class
|
||||
> inheritance. However, our type variable would be invariant and would not
|
||||
> deal with subtyping.
|
||||
> 2. Type variables cannot contain any type variable in their bound. For
|
||||
> example, `B = TypeVar('B', A, T3)` is not allowed.
|
||||
> 3. I did not really check the difference between the variable name and the
|
||||
> name parameter of `TypeVar`, so idk what would happen if they are
|
||||
> different. Please don't do that right now, would be fixed in later more
|
||||
> serious implementations.
|
||||
|
||||
For generic type variables, you can't really do much with them, other than
|
||||
passing them around in parameters, dealing with their list, etc.
|
||||
|
||||
For bounded type variables, if an operation is supported by all the possible
|
||||
values of the variable, we can use that directly:
|
||||
```python
|
||||
A = TypeVar('A', int32, int64)
|
||||
|
||||
def add(a: A, b: A) -> A:
|
||||
return a + b
|
||||
```
|
||||
|
||||
If an operation is supported by some possible values, we can use type guard:
|
||||
```python
|
||||
A = TypeVar('A', int32, list[int32])
|
||||
def add2(a: int32, b: A) -> a:
|
||||
if type(b) == int32:
|
||||
# b is int32 here
|
||||
return a + b
|
||||
else:
|
||||
# b is list[int32] here
|
||||
for x in b:
|
||||
a = a + b
|
||||
return a
|
||||
```
|
||||
|
||||
Note that we only support very simple kinds of type guards in this toy
|
||||
implementation. More specifically, the type guard has to meet the following
|
||||
conditions:
|
||||
1. The if statement must be of the form `type(*) == **` or `type(*) != **`.
|
||||
For example, `if type(b) == int32 or type(b) == list[int32]` is not allowed.
|
||||
2. The type of `*` must be a type variable. For example, `list[X]` is not
|
||||
allowed.
|
||||
|
||||
### Substitution
|
||||
The crucial constraint and assumption in our system is that, every
|
||||
(sub-)expressions must have their types fully determined, and cannot depend on
|
||||
statements/expressions after them. Hence, in a function call, every arguments
|
||||
@ -81,7 +157,7 @@ def sum_of_heads(a: list[Y], b: list[Y]) -> Y:
|
||||
|
||||
In this example, `a` has type `list[Y]`, so the algorithm would give a
|
||||
substitution `X -> Y` for the call `head(a)`, and similarly for `b`.
|
||||
As `Y` can only range over `int32` and `int64`, in the two valuations of `Y`,
|
||||
As `Y` can only range over `int32` and `int64`, in the two instances of `Y`,
|
||||
the return statement would have type
|
||||
* `int32 + int32 : int32 : Y` under `Y -> int32`, and
|
||||
* `int64 + int64 : int64 : Y` under `Y -> int64`.
|
||||
@ -96,6 +172,7 @@ def add(a: int32, b: I) -> int32:
|
||||
if type(b) == int32:
|
||||
return a + b
|
||||
else:
|
||||
# b must be list[int32] in this branch.
|
||||
for x in b:
|
||||
a = add(a, x)
|
||||
return a
|
||||
@ -105,7 +182,21 @@ add(1, [1, 2, 3])
|
||||
This one should type check. `I -> list[int32]` only affects 1 call,
|
||||
and the recursion inside could substitute `I -> int32`.
|
||||
|
||||
## Variable Scoping
|
||||
Example of a failure case:
|
||||
```python
|
||||
A = TypeVar('A')
|
||||
B = TypeVar('B')
|
||||
|
||||
def foo(a: A, b: A):
|
||||
pass
|
||||
|
||||
def bar(a: A, b: B):
|
||||
foo(a, b)
|
||||
```
|
||||
|
||||
This would fail. From the first argument, we have `A -> A`, and from the second
|
||||
argument we need `A -> B`. In general, we may have `A != B`, so there is no
|
||||
substitution that meets the requirement and the type check failed.
|
||||
|
||||
## Operator Override
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user