我正在为参与的研究项目实现小型DSL。由于这是几乎所有有关GADT的解释的示例,因此我认为这是真正开始使用它们的好时机。显而易见的表达式(算术等)可以很好地工作,但是我的DSL需要支持用户定义的功能,这正是我遇到的问题。
在没有GADT的情况下,我的表达式类型的结构看起来像这样(浓缩为一个最小的示例):
data Expr = IntConst Int
| BoolConst Bool
| Plus Expr Expr
| Times Expr Expr -- etc
| AndAlso Expr Expr -- etc
| Call String [Expr] -- function call!
转换为GADT时,我该如何表达Call
规则?
data Expr a where
IntConst :: Int -> Expr Int
BoolConst :: Bool -> Expr Bool
Plus :: Expr Int -> Expr Int -> Expr Int
Times :: Expr Int -> Expr Int -> Expr Int
AndAlso :: Expr Bool -> Expr Bool -> Expr Bool
Call :: ???
我首先想到的是,如果没有某些依赖于超级幻想的类型,这是不可能的,因为无法知道给定用户函数将接受哪些类型(还有返回类型,但是我可以通过使Call
来解决此问题返回Expr a
并专门从事施工现场)。我可以通过添加规则“擦除”类型来强制其进行类型检查
EraseInt :: Expr Int -> Expr a
EraseBool :: Expr Bool -> Expr a
,但是似乎我一开始就失去了使用GADT的好处。我还认为我可能会在rankN
中使用其他Call
多态性(某种存在性类型吗?),但我没有想到的事情似乎奏效。
帮助?
答案 0 :(得分:3)
也许您不需要走依赖路线即可获得所需的东西。如何将构建函数和调用函数分开的解决方案呢?这样,您就可以在构建函数时键入您的函数,从而可以进行类型检查的调用。
data Expr a where
IntConst :: Int -> Expr Int
BoolConst :: Bool -> Expr Bool
Plus :: Expr Int -> Expr Int -> Expr Int
Times :: Expr Int -> Expr Int -> Expr Int
AndAlso :: Expr Bool -> Expr Bool -> Expr Bool
Fun :: String -> Expr (a->b)
Call :: Expr (a->b) -> Expr a -> Expr b
-- type checks
test = Call (Fun "f" :: Expr (Int -> Int)) (IntConst 1)
-- doesn't type check
test' = Call (Fun "f" :: Expr (Int -> Int)) (BoolConst False)
对于具有多个参数的函数,您可以通过多次调用(即
)以咖喱的方式工作 Call (Call (Fun "f" :: Expr (Int->Int->Int)) (IntConst 1)) (IntConst 1)
或者您可以使用您的语言实现元组。
答案 1 :(得分:2)
您可以使用以下类型级别的列表制作另一个具有表示参数列表的GADT:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE PatternSynonyms #-}
data ArgList (as :: [*]) where
NilArgList :: ArgList '[]
ConsArg :: Expr a -> ArgList as -> ArgList (a ': as)
data Expr a where
IntConst :: Int -> Expr Int
BoolConst :: Bool -> Expr Bool
Plus :: Expr Int -> Expr Int -> Expr Int
Times :: Expr Int -> Expr Int -> Expr Int
AndAlso :: Expr Bool -> Expr Bool -> Expr Bool
Call :: String -> ArgList as -> Expr b
pattern x :^ y = ConsArg x y
infixr :^
example :: Expr Int
example =
Call "exampleFn" (IntConst 1 :^ BoolConst True :^ NilArgList
:: ArgList [Int, Bool])
您将需要为参数列表提供一些显式的类型签名(例如example
中的)。另外,结果类型Call
(forall b. Expr b
)有点尴尬,但是我不确定可以避免这种情况,除非您拥有比函数类型String
多的类型信息如果在那里有更多信息,则还可以将参数类型(as
)与函数的预期参数类型相关联。我认为我们需要有关您需要进一步处理的具体情况的更多详细信息。