Haskell for Lambda微积分,类型推理

时间:2013-12-05 15:40:13

标签: haskell types lambda functional-programming lambda-calculus

我在Haskell编程中的冒险并非都是史诗般的。我正在实现Simple Lambda Calculus,我很高兴完成SyntaxEvaluation以及Substitution,希望它们是正确的。剩下的是typing在红框内定义(如下图所示),我正在寻找指导。

标题

Figure 1 : Simple Lambda Calculus

如果我错了,请纠正我,

(1)但我收集的是(T-Var),返回给定变量x的类型。 Haskell中的什么构造返回type?我知道在prelude :t xmain = do,但我正在寻找一个在type_of下工作的人。

(2)如果我要定义一个函数type_of (Var x) :: type1 -> type2,我最有可能需要定义期望和返回类型,例如, type1

type2应该是通用的,type1必须是存储变量类型信息的任何对象类型。为此,我对如何定义type2Abstraction String Lambda感到失望。

(3)对于(T-APP)和(T-ABS),我假设我分别在Application Lambda Lambda和{{1}}上应用替换。简化形式的类型是返回的类型。这是对的吗?

提前致谢...

2 个答案:

答案 0 :(得分:11)

从简单类型的lambda演算中取消的关键是,lambda绑定器本身会对类型进行注释,每个lambda术语都有一个类型。 Pierce提供的输入规则是如何机械地类型检查表达式是良好类型的。 类型推断是他在本书后面介绍的主题,它正在从无类型表达式中恢复类型。

除此之外,皮尔斯在这个例子中没有提供的是几种地面类型(BoolInt),它们在实现算法时很有帮助,因此我们只需将它们附加到我们的定义也是如此。

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)]

空的环境Ø只是[]。当Pierce写Γ, x : T ⊢ ...时,他意味着扩展了环境,其中x的定义绑定到类型T。在Haskell中,我们将实现它:

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

要从TAPL编写检查器,我们实现了一个小错误monad堆栈。

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))

我们希望我们的类型检查器验证它是否具有输出类型TInt

但对于像以下这样的术语来说失败了:

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

由于TInt不等于(TInt -> TInt)

这就是确实要对STLC进行攻击。

答案 1 :(得分:6)

基本上。我相信这是来自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

所以我们抛弃这种类型的环境,并在我们去的时候扩展它。这里的输入规则很容易转换为算法,因为它们完全符合语法。 EG,对于术语M,只有一条规则得出结论为Env |- M : T

如果不是例如子类型,则会变得更加困难。