MODULES
(모듈: OPAQUE-TYPES)
Choi, Kwanghoon (최광훈)
Chonnam National University (전남대학교)
Overview: Modules That Declare Types
In the next module language, OPAQUE-TYPES, we allow interfaces to declare types as well.
2
module m1
interface
[ opaque t
zero : t
succ : (t -> t)
pred : (t -> t)
is-zero : (t -> bool)
]
body
…
Point1: This interface declares a type t, and some operations zero, succ, pred, and is-zero that operate on values of that type.
Overview: Modules That Declare Types
In the next module language, OPAQUE-TYPES, we allow interfaces to declare types as well.
3
module m1
interface
[ opaque t
zero : t
succ : (t -> t)
pred : (t -> t)
is-zero : (t -> bool)
]
body
…
Point2: Here t is declared to be an opaque type, meaning that code outside the module boundary does not know how values of this type are represented.
All the outside code knows is that it can manipulate values of type from m1 take t with the procedures from m1 take zero, from m1 take succ, etc.
Thus from m1 take t behaves like a primitive type such as int or bool.
Overview: Modules That Declare Types
In the next module language, OPAQUE-TYPES, we will introduce two kinds of type declarations: transparent and opaque. Both are necessary for a good module system.
4
Overview
Examples: Transparent types as abbreviation
5
initial-point : (int -> pairof int * int)
increment-x : (pairof int * int -> pairof int * int)
[Alices-points]
Bob and Charlie complains about this. They don’t want to have to write pairof int * int over and over again.
Overview
Examples: Transparent types as abbreviation
6
module Alices-points
interface
[ transparent point = pairof int * int
initial-point : (int -> point)
increment-x : (point -> point)
get-x : (point -> int)
… ]
body
[ type point = pairof int * int
… ]
Alice therefore rewrites her interface to use transparent type declarations. This simplifies her task, since she has less writing to do, and
It makes her collaborators’ tasks simpler, because in their implementations they can write abbreviated definitions.
[ transparent point = from Alice-points take point
foo = proc (p1 : point)
proc (p2 : point) … ]
Overview
Examples: Opaque types to enforce the boundary of abstraction
- Assume the points in Alice’s project happen to represent points on a metal track with a fixed geometry, so the x- and y-coordinates are not independent.
( 가정: 트랙의 모양이 이미 정해져 있어, 그 모양에 따라 x-좌표와 y-좌표가 결정)
- Alice’s implementation of increment-x carefully updates the y-coordinate to match the change in the x-coordinate.
7
Overview
Examples: Opaque types to enforce the boundary of abstraction
- Assume the points in Alice’s project happen to represent points on a metal track with a fixed geometry, so the x- and y-coordinates are not independent.
( 가정: 트랙의 모양이 이미 정해져 있어, 그 모양에 따라 x-좌표와 y-좌표가 결정)
- But Bob doesn’t know this, and so he writes his own procedure
without changing the x-coordinate correspondingly. Alice’s code no longer works correctly.
8
increment-y = proc ( p : point ) (p.x, -(p.y, -1))
Overview
Examples: Opaque types to enforce the boundary of abstraction
- Alice can solve this problem by making point an opaque data type.
9
module Alices-points
interface
[ opaque point
initial-point : (int -> point)
increment-x : (point -> point)
get-x : (point -> int)
… ]
body
[ type point = pairof int * int
… ]
Now Bob can create new points and can manipulate points, but he can no longer manipulate points using any procedure other than the ones in Alice’s interface. (cf. increment-y)
Overview: Transparent types
Also called concrete type declarations or type abbreviations
-
10
This program has type (int -> bool).
Overview: Transparent types
The declaration transparent t = int in the interface binds t to the type int in the rest of the interface, so we can write z : t.
More importantly, it also binds from m1 take t to int in the rest of the program. We call this a qualified type.
The scope of a declaration is the rest of the interface and the rest of the program after the module definition.
11
Overview: Transparent types
The definition type t = int in the body binds t to the type int in the rest of the module body, so we could write s = proc (x : t) … .
The scope of a definition is the rest of the body.
12
Overview: Opaque types
Also called abstract types
13
This program has ill-type in -(x,0) because the main program does not know that values of types from m1 take t are actually values of type int.
OK
Not OK
Overview: Opaque types
The declaration opaque t in the interface declares t to be the name of a new opaque type. An opaque type behaves like a new primitive type, such as int or bool.
The named type t is bound to this opaque type in the rest of the interface, and the qualified type, from m1 take t, is bound to the same opaque type in the rest of the program.
14
Overview: Opaque types
All the rest of the program knows about this type from m1 take t is that
- from m1 take z is bound to a value of that type, and that
- from m1 take s and from m1 take is-z? are bound to procedures that can manipulate values of that type.
This is the abstraction boundary. The type checker guarantees that the evaluation of an expression has type from m1 take t is safe, so that the value of the expression has been constructed only by these operations.
15
Overview: Opaque types
The definition type t = int in the body defines t to be a name for int inside the module body, but this information is hidden from the rest of the program, because the rest of the program gets its bindings from the module interface.
16
Overview: Opaque types
Also called abstract types
17
This gives a mechanism to enforce the distinction between the users of a data type and its implementation.
Now this program has type(from m1 take t -> bool).
By enforcing this abstraction boundary, the type checker guarantees that no program manipulates the values provided by the interface except through the procedures that the interface provides.
Overview: Opaque types
-
18
Overview: Opaque types
Example 8.8
19
There is no way the program can figure out that from colors take color is actually int, or that from colors take green is actually 1.
Overview: Opaque types
Example 8.9
20
This program has type from ints1 take t. It has value 10, but we can manipulate this value only through the procedure that are exported from ints1.
This module represents the integer k by the expressed value 5 * k.
from ints1 take succ
(from ints1 take succ
(fromt ints1 take zero))
Overview: Opaque types
Example 8.10
21
This program has type from ints2 take t. It has value -6.
Overview: Opaque types
Example 8.11
22
This program converts a value from the module back to a value of type int by a procedure to-int.
It has type int and has value 2.
The type of to-int is
( from ints1 take t -> int )
Overview: Opaque types
Example 8.12
23
Here is the same technique used with the implementation of arithmetic ints2.
It also has type int and value 2.
Later, we will show how to abstract over these two examples (8.11 and 8.12).
Overview: Opaque types
Example 8.13
24
We construct a module to encapsulate a data type of booleans. The booleans are represented as integers, but that fact is hidden from the rest of the program.
It has type from mybool take t, and has value 13.
OPAQUE-TYPES: Syntax (구문)
The grammar for OPAQUE-TYPES extends the grammar for SIMPLE-MODULES. (OPAQUE-TYPES 문법은 SIMPLE-MODULES 문법을 확장).
- We introduce transparent and opaque type declarations and qualified type references.
- Two new kinds of types: named types (like t) and qualified types (like from m1 take t)
25
Type -> identifier
Type -> from identifier take identifier
OPAQUE-TYPES: Syntax (구문)
- Two new kinds of declarations, for opaque and transparent types
- A new kind of definition: a type definition, which will be used to define both opaque and transparent types
26
Declaration -> opaque identifier
Declaration -> transparent identifier = Type
type identifier = Type
27
OPAQUE-TYPES: Abstract syntax tree (추상 구문 트리)
The AST datatype for transparent and opaque declarations
28
data Declaration =
ValDecl Identifier Type
| OpaqueTypeDecl Identifier
| TransparentTypeDecl Identifier Type
OPAQUE-TYPES: Abstract syntax tree (추상 구문 트리)
The AST datatype for type definitions
29
data Definition =
ValDefn Identifier Exp
| TypeDefn Identifier Type
x = 123
y = list (1,2,3)
z = bool
type t = int
type point = pairof int * int
OPAQUE-TYPES: Abstract syntax tree (추상 구문 트리)
The AST datatype for type names and qualified types
30
data Type =
TyInt
| TyBool
| TyFun Type Type
| TyName Identifier
| TyQualified Identifier Identifier
OPAQUE-TYPES: Abstract syntax tree (추상 구문 트리)
Parsing(파싱): Turn a program text(a sequence of characters) into an abstract syntax tree (프로그램 텍스트, 문자 나열을 분석해서 추상 구문 트리를 만들기)
31
Parsing(파싱)
OPAQUE-TYPES: Abstract syntax tree (추상 구문 트리)
Examples
32
OPAQUE-TYPES: Semantics (by Interpreter)
The expressed values, ExpVal, are the possible values of expressions (식을 계산해서 나올 수 있는 가능한 값, ExpVal)
- ExpVal = Int + Bool + Proc
- DenVal = ExpVal
Note: This is the same as that for CHECKED and SIMPLE-MODULES. (CHECKED언어나 SIMPLE-MODULES에서와 동일)
33
OPAQUE-TYPES: Semantics (by Interpreter)
The interpreter doesn’t look at types or declarations, so the only changes to the interpreter is to make it ignore type definitions!
- A modification of the SIMPLE-MODULE interpreter:
34
Just ignore the type definition.
OPAQUE-TYPES: Semantics (by Interpreter)
Example
35
OPAQUE-TYPES: Semantics (by Type Checker)
The changes to the checker are more substantial, since all the manipulations involving types must be extended to handle the new types.
36
equalType (TyName “t”) TyInt ?
equalType (TyQualified “m1” “t”)) TyInt ?
“t” should be expanded.
We should lookup “m1”, and “t” should be expanded.
OPAQUE-TYPES: Semantics (by Type Checker)
The changes to the checker are more substantial, since all the manipulations involving types must be extended to handle the new types. (Cont.)
37
equalType (TyName “t”) TyInt ?
equalType (TyQualified “m1” “t”) TyInt ?
“t” should be expanded.
We should not lookup “m1”, nor should “t” be expanded.
OPAQUE-TYPES: Semantics (by Type Checker)
First, we introduce a systematic way of handling opaque and transparent types. An opaque type behaves like a primitive type while transparent types behave exactly like their definitions.
=> So, every type is equivalent to one that is given by the following grammar:
- Type ::= int | bool | from m take t | ( Type -> Type )
We call a type of this form an expanded type.
38
OPAQUE-TYPES: Semantics (by Type Checker)
We next extend type environments to handle new types. Our type environments will bind each named type or qualified type to an expanded type.
39
data TyEnv =
Empty_tyenv
| Extend_tyenv Identifier Type TyEnv
| Extend_tyenv_with_module Identifier Interface TyEnv
| Extend_tyenv_with_type Identifier Type TyEnv
Invariant: type will always be made to be an expanded type.
type name
var name
module name
OPAQUE-TYPES: Semantics (by Type Checker)
We next extend type environments to handle new types. Our type environments will bind each named type or qualified type to an expanded type.
40
We must be sure to call expand_type whenever we extend the type environment in order to maintain this invariant.
OPAQUE-TYPES: Semantics (by Type Checker)
Where should we call expand_type to maintain this invariant in extending a type environment.
- In the checker: type_of
e.g., extend_tyenv x ty tenv => extend_tyenv x (expand_type ty tenv) tenv
- When processing a list of definitions: defns_to_decls
- When adding a module to the type environment: add_module_defns_to_tyenv
Later we will see how types and interfaces are expanded.
41
OPAQUE-TYPES: Semantics (by Type Checker)
The structure of the checker:
- In type_of_program, a type environment is built for the body expression, using add_module_defns_to_tyenv.
- The procedure computes an actual interface by interface_of, and compares it with the declared/expected interface by sub_iface.
- If good, the procedure adds a new type environment with the expanded interface of the declared one computed by expand_iface.
42
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure add_module_defns_to_tyenv:
-
43
m has (interface) type iface’ expanded from iface.
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure interface_of uses defns_to_decls:
-
44
ty is already expanded through type_of exp tyenv.
ty should be expanded.
TypeDefn var ty is turned into TransparentTypeDecl var ty since in the body all type bindings are transparent.
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure expand_iface:
-
45
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure expand_decls:
-
46
OpaqueTypeDecl x turns to TransparentTypeDecl x expanded_type !!
TransparentTypeDecl x ty turns to TransparentTypeDecl x expanded_type !!
Let* scoping: each declaration in a set of declarations is in scope in all the following declarations.
e.g., expand_decls … new_itnernal_env
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure expand_decls : Let* scoping: each declaration in a set of declarations is in scope in all the following declarations.
47
module m1
interface
[ opaque t
transparent u = int
transparent uu = (t -> u)
f : uu
… ]
body
[ … ]
module m1
interface
[ transparent t = from m1 take t
transparent u = int
transparent uu = (from m1 take t -> int)
f : (from m1 take t -> int)
… ]
body
[ … ]
expand_decls : [Declaration] ==> [Declaration]
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure expand_decls : Let* scoping: each declaration in a set of declarations is in scope in all the following declarations.
48
module m1
interface
[ opaque t
transparent u = int
transparent uu = (t -> u)
f : uu
… ]
body
[ … ]
module m1
interface
[ transparent t = from m1 take t
transparent u = int
transparent uu = (from m1 take t -> int)
f : (from m1 take t -> int)
… ]
body
[ … ]
expand_decls : [Declaration] ==> [Declaration]
A
In (A), the type environment should bind
t to from m1 take t,
u to int,
uu to (from m1 take t -> int).
(We call it the internal type environment.)
NB. this procedure creates only transparent declarations, since its purpose is to create a data structure in which qualified types can be looked up.
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure sub_iface:
-
49
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure sub_decls to handle the two new kinds of declarations.
- We must deal with the scoping relations inside a set of declarations.
- extend_tyenv_with_decl will do the job.
50
[ transparent t = int
x : bool
y : t ]
[ y : int ]
<:
We need to know that t refers to type int when we get to the declaration of y.
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure sub_decls:
-
51
extend_tyenv_with_decl translates a declaration decl1 to an appropriate extension of the type environment.
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure sub_decl:
- How do we compare declarations?
- NB. equalExpandedType
52
They are both value declarations, and their types match.
They are both opaque type declarations.
They are both transparent type declarations, and their definitions match.
decl1 is a transparent type declaration, and decl2 is an opaque type declaration.
transparent t=int <: opaque t (O)
opaque t <: transparent t=int (X)
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure extend_tyenv_with_decl:
-
53
This creates a fresh module name!
It maintains the invariant that all types in the type environment are expanded.
OPAQUE-TYPES: Semantics (by Type Checker)
The procedure equalExpandedType:
-
- E.g.,
54
[ transparent t = int x : bool y : t ] <: [ y : int ]
OPAQUE-TYPES: Projects
-
55
Tokens.hs | Declare tokens (구문의 최소 단위 선언) |
Lexer.hs | Turn a text (char seq.) into a token sequences( 텍스트를 토큰으로 분리) |
Expr.hs | Declare abstract syntax trees (구문 트리 선언) |
Env.hs | Declare the environment types with modules (모듈을 포함한 환경 선언) |
Parser.hs | Turn the token seq. into an abstract syntrax tree (토큰들을 구문 트리로) |
TyEnv.hs | Declare environments with module types (모듈 타입/인터페이스를 포함한 환경 타입 선언), |
TypeCheck.hs | Type checker (타입 검사) |
Interp.hs | An interpreter (해석기) |
EitherState.hs | The combination of Either and State (Either와 State 혼합 타입 유틸리티) |
OPAQUE-TYPES: Examples
-
56
OPAQUE-TYPES: Examples
- To be updated soon.
57
OPAQUE-TYPES: Exercises
-
58
OPAQUE-TYPES: Exercises
-
59