Haskell 将简单类型化语言的非类型化AST转换为GADT
我有一个代表简单语言AST的ADT:Haskell 将简单类型化语言的非类型化AST转换为GADT,haskell,gadt,Haskell,Gadt,我有一个代表简单语言AST的ADT: data UTerm = UTrue | UFalse | UIf UTerm UTerm UTerm | UZero | USucc UTerm | UIsZero UTerm 此数据结构可以表示不遵循类型的无效术语 语言规则,比如UIsZero-UFalse,所以我想使用GADT 强制执行良好类型: {-# LANGUAGE GADTs #-} data TTerm a where TT
data UTerm = UTrue
| UFalse
| UIf UTerm UTerm UTerm
| UZero
| USucc UTerm
| UIsZero UTerm
此数据结构可以表示不遵循类型的无效术语
语言规则,比如UIsZero-UFalse
,所以我想使用GADT
强制执行良好类型:
{-# LANGUAGE GADTs #-}
data TTerm a where
TTrue :: TTerm Bool
TFalse :: TTerm Bool
TIf :: TTerm Bool -> TTerm a -> TTerm a -> TTerm a
TZero :: TTerm Int
TSucc :: TTerm Int -> TTerm Int
TIsZero :: TTerm Int -> TTerm Bool
我的问题是对子宫进行类型检查并将其转换为TTerm。我的第一次
思想是utrem->Maybe(tterma)
,但这当然不起作用,因为
它不适用于所有a
s。我甚至不知道会是什么类型,因为
我们不知道a是Int还是Bool。然后我想我可以写一本书
a的每个可能值的不同类型检查功能:
import Control.Applicative
typecheckbool :: UTerm -> Maybe (TTerm Bool)
typecheckbool UTrue = Just TTrue
typecheckbool UFalse = Just TFalse
typecheckbool (UIsZero a) = TIsZero <$> typecheckint a
typecheckbool _ = Nothing
typecheckint :: UTerm -> Maybe (TTerm Int)
typecheckint UZero = Just TZero
typecheckint (USucc a) = TSucc <$> typecheckint a
typecheckint (UIf a b c) = TIf <$> typecheckbool a <*> typecheckint b <*> typecheckint c
typecheckint UTrue = Nothing
typecheckint UFalse = Nothing
typecheckint (UIsZero _) = Nothing
导入控件。应用程序
typecheckbool::utem->Maybe(TTerm Bool)
typecheckbool-UTrue=Just-TTrue
typecheckbool-UFalse=仅TFalse
typecheckbool(UIsZero a)=TIsZero typecheckint a
typecheckbool=无
typecheckint::utem->Maybe(TTerm Int)
typecheckint-UZero=Just-TZero
typecheckint(USucc a)=TSucc typecheckint a
typecheckint(UIf a b c)=TIf typecheckbool a typecheckint b typecheckint c
typecheckint-UTrue=无
typecheckint-UFalse=无
typecheckint(UIsZero u)=无
这适用于某些情况,适用于TIf需要其
结果和替代是INT(但如果真的是TFalse TTrue
实际上是
完全有效),并且我们知道表达式的目标类型
打字
从子宫到子宫的正确转换方式是什么?标准技术是定义存在类型:
data ETerm_ where
ETerm_ :: TTerm a -> ETerm
在这种情况下,您可能还需要一些术语级别的证据,您拥有哪种类型的证据;e、 g
data Type a where
TInt :: Type Int
TBool :: Type Bool
那么真正的etrem
将如下所示:
data ETerm where
ETerm :: Type a -> TTerm a -> ETerm
类型检查的有趣案例如下
typeCheck (UIf ucond ut uf) = do
ETerm TBool tcond <- typeCheck ucond
ETerm tyt tt <- typeCheck ut
ETerm tyf tf <- typeCheck uf
case (tyt, tyf) of
(TBool, TBool) -> return (ETerm TBool (TIf tcond tt tf))
(TInt , TInt ) -> return (ETerm TInt (TIf tcond tt tf))
_ -> fail "branches have different types"
typeCheck(UIf ucond ut uf)=do
ETerm TBool tcond fail“分支具有不同的类型”
作为@DanielWagner答案的一个次要补充,您可能希望分解类型相等检查,例如
...
case (tyt, tyf) of
(TBool, TBool) -> return (ETerm TBool (TIf tcond tt tf))
(TInt , TInt ) -> return (ETerm TInt (TIf tcond tt tf))
_ -> fail "branches have different types"
一种方法是使用平等证人:
import Data.Type.Equality
typeEq :: Type a -> Type b -> Maybe (a :~: b)
typeEq TInt TInt = Just Refl
typeEq TBool TBool = Just Refl
typeEq _ _ = Nothing
typeCheck :: UTerm -> Maybe ETerm
typeCheck (UIf ucond ut uf) = do
ETerm TBool tcond <- typeCheck ucond
ETerm tyt tt <- typeCheck ut
ETerm tyf tf <- typeCheck uf
case typeEq tyt tyf of
Just Refl -> return (ETerm tyt (TIf tcond tt tf))
_ -> fail "branches have different types"
但是,我想,除非您尝试对非常高级的类型(例如GADT)建模,否则可能不需要这样做
上面的代码使用空格检查eq
可以具有的所有可能值。由于它具有类型,例如Int:~:Bool
,并且没有与该类型匹配的构造函数,因此我们没有eq
的可能值,因此不需要case分支。这不会触发详尽性警告,因为确实没有未处理的案例(OT:我希望这些警告是实际错误)
除了使用
EmptyCase
之外,你还可以使用case eq of u->undefined
,但是在上面这样的证明术语中使用bottoms是可疑的。顺便说一句,你当然可以添加typecheckbool(UIf a b c)
case来解决“但是TIf TTrue TFalse TTrue
实际上是完全有效的”关注点。这很有帮助,特别是如果有两种以上的类型,谢谢。为什么doblock()中的模式匹配不起作用,而case语句起作用呢?Just Refl您能给我一个提示,说明如何证明a:~:b)->Void
?如果术语/类型语言包含变量,这仍然可以实现吗?类型
数据类型能代表这样一种语言吗?@user2407038定义一种比Haskell的类型系统更具表现力的语言肯定是可能的;然后,您将无法使用Haskell的类型检查器来检查该语言中的术语是否正确。然而,在大多数情况下,如果您的语言的类型系统没有Haskell的表达能力强,那么您可以说服Haskell为您检查您的术语——当然,越努力,您的系统就越令人兴奋。
{-# LANGUAGE EmptyCase #-}
typeEq2 :: Type a -> Type b -> Either (a :~: b) ((a :~:b) -> Void)
typeEq2 TInt TInt = Left Refl
typeEq2 TInt TBool = Right (\eq -> case eq of)
typeEq2 TBool TBool = Left Refl
typeEq2 TBool TInt = Right (\eq -> case eq of)