我在Haskell编程中的冒险并不都是史诗般的。我正在实现简单的Lambda演算,我很高兴完成了语法
、求值
以及替换
,希望它们是正确的。剩下的是键入
,就像红色框中定义的那样(在下图中),我正在为此寻找指导。
如果我说错了请指正,
(1)但是我发现(T-Var)
返回给定变量X
的类型。Haskell中的什么构造返回type
?我知道在prelude
中是:tx
,但我正在寻找一个在main=do
下工作的。
(3)对于(T-APP)和(T-ABS),我假设我分别对抽象字符串Lambda
和应用程序lambdalambda
应用程序应用了替换。缩减窗体的类型是返回的类型。那是正确的吗?
提前谢谢...
从简单类型化的lambda演算中得到的关键是,类型是在lambda绑定器本身上注释的,每个lambda术语都有一个类型。Pierce提供的键入规则是如何机械地键入检查表达式是否键入良好。类型推断是他在这本书后面讨论的一个主题,这是从非类型化表达式中恢复类型。
除此之外,Pierce在这个例子中没有给出一些基本类型(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)
type Env = [(Sym, Type)]
extend :: Env -> (Sym, Type) -> Env
extend env xt = xt : env
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))
其中是用于定义替换的函数。 然而,当我试图使用beta约简来约简表达式时,我得到了一个非穷举模式错误,我不明白为什么。我能做的修复它是在底部添加一个额外的大小写,如下所示: 但是,如果我这样做了,那么lambda表达式根本不会被缩减,这意味着函数的输入和输出是相同的。 如果使用第一个函数而不添加最后一个大小写,则为非穷尽模式 或完全相同的表达式App(ABS1(ABS2(VAR1)))(VAR3)
我最近发现,通过lambda中的值捕获对象,意味着labmda主体(即lambda的数据成员)中的变量也是 例如: §8.1中提到了这种行为。5.2在C 17草案中: 对于通过复制捕获的每个实体,在闭包类型中声明一个未命名的非静态数据成员。未指定这些成员的声明顺序。如果实体是对对象的引用,则此类数据成员的类型为引用类型;如果实体是对函数的引用,则为对引用函数类型的左值引用;或者以其他方式为相应捕获
部分答案可能来自领域理论。如果我理解正确的话,的元素并不全是集合论函数(根据康托定理,这些函数比大),而只是连续函数。如果是,定义这种连续性的拓扑是什么?为什么类型的Haskell术语对它是连续的?
我定义了一个自定义GADT,其中类型构造函数对类型变量有一个类型类约束,如下所示: 我假设我不必提到约束,因为它是在GADT定义中声明的。我唯一能想到的部分原因是GADT在函数中的位置。我对此不太了解,但据我所知,在中处于正位置,在中处于负位置。 我什么时候必须明确地提到类型类约束,什么时候GHC自己根据GADTs类型构造函数上的约束来解决它?
我发现优先级和关联性是一个很大的障碍,让我理解语法在haskell代码中试图表达的内容。 例如, 通过实验,我终于明白了, 你们谁能提供一些参考,让语法更容易掌握吗?