본문 바로가기
학교/CPL

Lecture 11: Type Checking

by Hongwoo 2024. 4. 6.
반응형

목차

Learning Objectives

- Explain the difference between a type system and a type checker

- Analyze programs as being well-typed or ill-typed

- Explain and motivate "type safety"

- Implement a simple type checker for a language with let expressions

 

 

Why Type Check?

Type checking helps rule out errors

Example: 

 

 

Type System vs Type Checker

type system is a specification of a set of syntactic restrictions on well-formed programs

type checker is an implementation of a type system

E.g. the program on the right is syntactically well-formed according to the grammar, but may not be well-typed.

 

Type system: syntactic restrictions: they characterize what programs are well-typed based on the syntax of the program

 

 

Well-Typed?

 

1. Well-typed (Type checked)

2. Won't type check because we are using integer where boolean is expected

3. Depends on what x is. (If x is defined and is integer, it is well-typed) String is also fine → concatenation

 

Well-Typed?

1. Scala can't decide if it knows what x should be. Cannot infer the type of x (Need more information)

2. Type checked (well-typed). 만약에 return type 없으면: Doesn't type check: fac needs return type

3. Not well-typed: could not infer the type of self. 

 

Typing is subtle (미묘한) 

How do we specify what it means for a program to be well-typed? 

→ Type system

 

 

 

 

Γ: type context 

1. n is well typed and has the type Num

2. Plus expressions: (+ e1 e2) is well typed and has the type Number if e1 has type number and e2 has type number

3. Let expression is well-bound if it has t2 whenever e1 has type t1 under the same type context as the original let expression 

Once I know that e1 has type t1 then I know the e2 has type t2 under the type context where I extend the type context by adding x with type t1. 

4. If we encounter a variable x has some type t if I look in the type context and find x has type t 

 

Not well-typed according to the type context as Num is required

 

 

Type Safety

Type safety is a formal statement that characterizes what behaviors a type system is supposed to rule out

For example, the two typing rules below for arithmetic expressions rule out a runtime type error

 

nv: well typed and correspond to the type context

 

 

여기를 보면 다음과 같은 것들을 알 수 있다.

1. nil의 타입은 List t이다

2. (cons e1 e2)의 타입은 List t이다.

3. (cons e1 e2)에서 e1의 타입은 t여야하고 e2의 타입은 List t이어야 한다 (nil도 가능).

따라서, (cons 1 2)는 not well-typed이고 (cons  1  (cons  2  nil))은 well-typed.

4. e가 List t일 때, (head e)의 타입은 t가 된다.

5. e가 List t일 때, (tail e)의 타입은 Lsit t이다.

 

 

For Scala:

Any well-typed program either:

- returns a value of the expected type

- throws a programmer-raised exception

- crashes with a pattern match failure

- crashes with an invalid cast exception

- non-terminates

 

 

Casting in Scala

→ In Scala, we can cast any value of any type to anything else  (C는 B와 아무 관련이 없어도 Cast는 가능)

 

D.b 하면 error (crashes with an invalid cast exception)

 

 

 

Casting in Java

 

 

Type Safety for Java

Any well-typed program either:

- returns a value of the expected type

- throws an unchecked exception

- crashes with an invalid upcast exception

- non-terminates

 

 

 

Type Checking vs Type Inference

Specifying types can be automated

Type inference lets us infer the type of programs, without having to provide type hints up-front

 

 

Type Inference in Scala

 

 

 

Type Inference in Haskell

 

 

 

Typing Concerns

Type safety = the guarantee we get from a type system

A type checker is:

Sound if it only accepts programs that are well-typed according to the type system it implements

Complete if any program that is well-typed according to the type system is also accepted by the implementation

Type checking is undecidable if we cannot write a type checker that is both sound, complete, and guaranteed to terminate

 

 

Code

tnv: TEnv (Type Environment)

Purpose of function tc: Given an expression e and type environment tnv, should return a type or runtime exception.

 

sealed abstract class Expr
case class Num(i: Int) extends Expr
case class Plus(e1: Expr, e2: Expr) extends Expr
case class Let(x: String, e: Expr, body: Expr) extends Expr
case class Id(x: String) extends Expr
case class True() extends Expr
case class False() extends Expr
case class If(c: Expr, t: Expr, e: Expr) extends Expr

// NEW
case class Fd(x: String, t: Type, e: Expr) extends Expr
case class App(e1: Expr, e2: Expr) extends Expr


sealed abstract class Type
case class NumT() extends Type
case class BoolT() extends Type

// NEW
case class FunT(params: List[Type], ret: Type) extends Type

def tc(e: Expr, tnv: List[(String, Type)]): Type = e match {
  case Num(i) => NumT()
  case Plus(e1, e2) => (tc(e1, tnv), tc(e2, tnv)) match {
    case (NumT(), NumT()) => NumT()
    case p =>
      throw RuntimeException("plus expression expects two numbers, but type checking found: " + p)
  }
  case Let(x, e, body) => {
    val t1 = tc(e, tnv)
    tc(body, (x, t1) :: tnv)
  }
  case Id(x) => lookup(x, tnv)
  case True() => BoolT()
  case False() => BoolT()
  case If(c, t, e) => tc(c, tnv) match {
    case BoolT() => (tc(t, tnv), tc(e, tnv)) match {
      case (t1, t2) if t1 == t2 => t1
      case p => throw RuntimeException("expected branches of if-expression to have the same type, but type checking found: " + p)
    }
    case t => throw RuntimeException("expected condition of if-expression to be boolean typed, but type checking found: " + t)
  }
  case Fd(pxs, e) =>
    val t1 = tc(e, (x, t) :: tnv)
    FunT(t, t1)
  case App(e1, e2) =>
    // check that e1 has type t1 -> t2
    // check that e2 has type t1
    // return t2

}

def lookup(x: String, tnv: List[(String, Type)]): Type = tnv match {
  case Nil => throw RuntimeException("error - free variable: " + x)
  case (y, t) :: tnv if (x == y) => t
  case _ :: tnv => lookup(x, tnv)
}

 

 

1. case Num(i) → just return NumT()

2. case Plus(e1, e2) → if type of e1 is NumT() and e2 is NumT(), return NumT(). Otherwise, exception

3. case True(), False() → they are both boolean so return BoolT()

4. case If(c, t, e) → first type check c and if it's boolean, check if t and e have the same type. If they do, return that type. Otherwise, exception.

반응형

'학교 > CPL' 카테고리의 다른 글

Lecture 12-13: Objects  (0) 2024.04.10
CPL 5: Parallel and Concurrent  (0) 2024.04.09
Lecture 9-10: Parallel and Concurrent  (0) 2024.04.06
CPL 4: Mutation and State  (0) 2024.04.05
Lecture 7-8: Mutation and State  (0) 2024.04.01

댓글