【发布时间】:2016-05-03 00:41:21
【问题描述】:
我正在用 Haskell 编写一种特定领域的语言,并且已经确定 具有两个 AST 的设计:一个初始的无类型表示语法和 代表一切的最终类型。我把最后一个写成 GADT 以获得更好的类型检查。
我认为它几乎在工作,但我在编写一个函数时遇到了问题 转换初始 -> 最终(检查类型,以及其他一些未显示的内容, 就像所有引用都对应一个变量)。
这是一个简化的例子:
{-# LANGUAGE GADTs, StandaloneDeriving #-}
module Main where
-- untyped initial AST
data Untyped
= UNum Int
| UStr String
| UAdd Untyped Untyped
deriving (Show, Eq)
-- typed final AST
data Typed a where
TNum :: Int -> Typed Int
TStr :: String -> Typed String
TAdd :: Typed Int -> Typed Int -> Typed Int
deriving instance Eq (Typed a)
deriving instance Show (Typed a)
-- wrapper that allows working with a `Typed a` for any `a`
data TypedExpr where
TypedExpr :: Typed a -> TypedExpr
这是我对check 函数的尝试。基本情况很简单:
check :: Untyped -> Either String TypedExpr
check (UNum n) = Right $ TypedExpr $ TNum n
check (UStr s) = Right $ TypedExpr $ TStr s
-- check (Uadd e1 e2) = ???
但是如何添加Add?它可以递归地评估子表达式
Either String (TypedExpr (Typed a)) 类型的值,但我没有设法
打开包装,检查类型是否对齐(@987654332@s 都应该是 Ints),然后
之后重新包装。我计划用大模式匹配来做这一切,但是 GHC
不同意:
My brain just exploded
I can't handle pattern bindings for existential or GADT data constructors.
Instead, use a case-expression, or do-notation, to unpack the constructor.
here已经解释过了,但我没看懂解释。它似乎 我不想要模式匹配。
更新:我一定是在不知不觉中搞砸了其他事情。模式匹配有效,如 Nikita 所示。
所以我摆弄着试图强迫事情进入
正确的形状,但还没有得到任何实质性的东西。如果这些是
只是Either String SomeValue 我想使用应用程序,对吗?是吗
可以向它们添加另一个级别的展开+类型检查吗?我怀疑this answer 接近我想要的,因为问题非常相似,但我还是不明白。 This 也可能是相关的。
更新:That first answer 毕竟正是我想要的。但是直到 chi 在没有额外的 Type 类型的情况下编写下面的中间版本时,我才知道如何。这是一个有效的解决方案。诀窍是用一个新类型标记TypedExprs,该类型仅表示Typed a 的return 类型(a):
data Returns a where
RNum :: Returns Int
RStr :: Returns String
-- extend TypedExpr to include the return type
data TypedExpr2 where
TypedExpr2 :: Returns a -> Typed a -> TypedExpr2
这样check 不必知道每个子表达式是否是原始的
TNum 或返回 TNum 的函数(如 Add):
check :: Untyped -> Either String TypedExpr2
check (UNum n) = Right $ TypedExpr2 RNum (TNum n)
check (UStr s) = Right $ TypedExpr2 RStr (TStr s)
check (UAdd u1 u2) = do
-- typecheck subexpressions, then unwrap by pattern matching
TypedExpr2 r1 t1 <- check u1
TypedExpr2 r2 t2 <- check u2
-- check the tags to find out their return types
case (r1, r2) of
-- if correct, create an overall expression tagged with its return type
(RNum, RNum) -> return $ TypedExpr2 RNum $ TAdd t1 t2
_ -> Left "type error"
GHC 足够聪明,知道任何TypedExpr2 中的两个as 必须匹配,
因此,如果您尝试在
结尾。太棒了!
【问题讨论】:
-
this 这样的东西怎么样?有关详细说明,请参阅 gergo.erdi.hu/blog/…。
标签: haskell pattern-matching typechecking applicative gadt