Haskell для лямбда-исчисления, тип выведения

мое приключение в программировании Haskell не было эпическим. Я реализую простое лямбда-исчисление, и я рад, что закончил Syntax, Evaluation, а также Substitution, надеясь, что они правильные. Остается typing как определено внутри красной коробки (на рисунке ниже), для которого я ищу руководство.

заголовок

Figure 1 : Simple Lambda Calculus

пожалуйста, поправьте меня, если я ошибаюсь,

(1) но я понял, что (T-Var), возвращает тип данных переменной x. Какая конструкция в Haskell возвращает type ? Я знаю это в prelude это :t x, но я ищу тот, который работает под main = do.

(2) Если бы я должен был определить функцию type_of, скорее всего, мне нужно определить ожидаемый и возвращаемый тип, в качестве примера, type_of (Var x) :: type1 -> type2

type1 должно быть общим и type2 должен быть любым типом объекта, который хранит информацию о типе переменной. Ради этого я потерян. о том, как определить type1 и type2.

(3) для (T-APP) и (T-ABS), я предполагаю, что применяю замену на Abstraction String Lambda и Application Lambda Lambda соответственно. Тип сокращенной формы-возвращаемый тип. Это верно?

спасибо заранее...

2 ответов


ключевая вещь, которую нужно забрать из просто типизированного лямбда-исчисления, заключается в том, что типы аннотируются на самих лямбда-связывателях, каждый лямбда-термин имеет тип. Правила набора текста, которые предоставляет Пирс, - это как механически type-check что выражение хорошо набирается. вывод типа это тема, которую он охватывает позже в книге, которая восстанавливает типы из нетипизированных выражений.

в стороне, что Пирс не дает в этом примере пара типов грунта (Bool, Int), которые полезны при реализации алгоритма, поэтому мы просто добавим их к нашему определению.

t = x
  | λ x : T . t
  | t t
  | <num>
  | true
  | false

T = T -> T
  | TInt
  | TBool

если мы переведем это в Haskell, мы получим:

type Sym = String

data Expr
    = Var Sym
    | Lam Sym Type Expr
    | App Expr Expr
    | Lit Ground
     deriving (Show, Eq, Ord)

data Ground = LInt Int
            | LBool Bool
            deriving (Show, Eq, Ord)

data Type = TInt
          | TBool
          | TArr Type Type
          deriving (Eq, Read, Show)

на Γ это прокалывает потоки через уравнения для среды типа, которую мы можем представить в Haskell как простую структуру списка.

type Env = [(Sym, Type)]

пустая среда Ø затем просто []. Когда Пирс пишет Γ, x : T ⊢ ... он имеет в виду среду, расширенную с определением x привязан к типу T. В Haskell мы бы реализовали его следующим образом:

extend :: Env -> (Sym, Type) -> Env
extend env xt = xt : env

чтобы написать проверку из TAPL, мы реализуем небольшой стек монады ошибок.

data TypeError = Err String deriving Show

instance Error TypeError where
    noMsg = Err ""

type Check a = ErrorT TypeError Identity a

check :: Env -> Expr -> Check Type
check _ (Lit LInt{}) = return TInt
check _ (Lit LBool{}) = return TBool

--  x : T ∈ Γ
--  ----------
--  Γ ⊦ x : T

check env (Var x) = case (lookup x env) of
    Just e  -> return e
    Nothing -> throwError $ Err "Not in Scope"

--  Γ, x : T ⊦ e : T'
--  --------------------
--  Γ ⊦ λ x . e : T → T'

check env (Lam x t e) = do
  rhs <- (check (extend env (x,t)) e)
  return (TArr t rhs)

--  Γ ⊦ e1 : T → T'   Γ ⊦ e2 : T
--  ----------------------------
--  Γ ⊦ e1 e2 : T'

check env (App e1 e2) = do
  t1 <- check env e1
  t2 <- check env e2
  case t1 of
     (TArr t1a t1r) | t1a == t2 -> return t1r
     (TArr t1a _) -> throwError $ Err "Type mismatch"
     ty -> throwError $ Err "Trying to apply non-function"

runCheck :: Check a -> Either TypeError a
runCheck = runIdentity . runErrorT

checkExpr :: Expr -> Either TypeError Type
checkExpr x = runCheck $ check [] x

когда мы называем checkExpr в выражении мы либо возвращаем допустимый тип выражения, либо TypeError указывает, что не так с функцией.

например, если мы имеем термин:

(λx : Int -> Int . x) (λy : Int. y) 3
App (App (Lam "x" (TArr TInt TInt) (Var "x")) (Lam "y" TInt (Var "y"))) (Lit (LInt 3))

мы ожидаем, что наш type checker подтвердит, что он имеет тип вывода TInt.

но не, например:

(λx : Int -> Int . x) 3
App (Lam "x" (TArr TInt TInt) (Var "x")) (Lit (LInt 3))

С TInt не равно (TInt -> TInt).

это все, что действительно нужно для проверки STLC.


в принципе. Я считаю, что это из TAPL (это похоже на таблицу из TAPL, по крайней мере), поэтому есть глава, посвященная алгоритмической проверке типов. Но это по существу идет как

typeOf :: TypeEnv -> Term -> Type
typeOf typeEnv (Var x)   = x `lookup` typeEnv
typeOf typeEnv (Abs var ty x) = ty `Arrow` typeOf ((x, ty) `extending` typeEnv) x
typeOf typeEnv (App f arg) = case typeOf f of
  Arrow inp out | inp == argT -> out
  _ -> Fail Some How
  where argT = typeOf typeEnv arg

поэтому мы бросаем вокруг этого типа среды и расширить его, как мы идем. Правила набора текста здесь легко перевести в алгоритм, потому что они точно соответствуют синтаксису. Например, на срок M существует ровно одно правило с выводом, что Env |- M : T.

это становится намного сложнее, когда это не так, например, субтипы.