如何用GADT表示变量类型的构造函数列表?

时间:2018-12-03 17:04:47

标签: haskell gadt

我正在为参与的研究项目实现小型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多态性(某种存在性类型吗?),但我没有想到的事情似乎奏效。

帮助?

2 个答案:

答案 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中的)。另外,结果类型Callforall b. Expr b)有点尴尬,但是我不确定可以避免这种情况,除非您拥有比函数类型String多的类型信息如果在那里有更多信息,则还可以将参数类型(as)与函数的预期参数类型相关联。我认为我们需要有关您需要进一步处理的具体情况的更多详细信息。