是否有可能为同一个构造函数有不同的行为?

时间:2016-11-18 18:24:43

标签: haskell dependent-type

我正在写一个SQL解释器。我需要在编译时区分不正确的表达式和运行时错误。

我将举例说明一些应该是格式正确的内容,但可能会在运行时失败。

SELECT $ [ColumnName "first_name" `AS` "name"] `FROM` TABLE "people.csv" `WHERE` (ColumnName "age" `Gte` LiteralInt 40)

我想专注于表达:

(ColumnName "age" `Gte` LiteralInt 40)

这应该通过类型检查器。但是,说“年龄”并不包含可以表示为LiteralInt的内容。

所以我希望Gte能够生成类似IO Bool的东西(暂不考虑现在的异常处理)。

但我并不总是需要Gte来制作IO Bool。如果我有这样的事情:

(LiteralInt 40 `Gte` LiteralInt 10)我只需要一个Bool。 或类似的东西: (LiteralInt 40 `Gte` LiteralBool True)需要在编译时失败。

所以,我一直在玩弄数据系列和GADT,并且已经陷入了许多死胡同,如果我解释它们就会混淆这种情况。

我的问题是否有意义,如果有的话,我可以通过哪种方式进行调查,从而找到解决方案?

1 个答案:

答案 0 :(得分:7)

  

所以我希望Gte能够生成类似IO Bool的东西(暂不考虑现在的异常处理)。

     

但我并不总是需要Gte来制作IO Bool

这是不可能的(也不是可取的)。 Gte必须始终返回相同的类型。此外,您可能希望将查询的构造与其执行分开......

  

或类似的东西:(LiteralInt 40 `Gte` LiteralBool True)需要在编译时失败。

现在 更合理。如果您决定沿着这条路走下去,您甚至可以使用新的TypeError功能自定义GHC报告的类型错误。但是,坚持只涉及LiteralIntLiteralBoolGte的简单示例,只需使用GADT即可执行以下操作:

{-# LANGUAGE GADTs #-}

data Expr a where
  LiteralInt :: Int -> Expr Int
  LiteralBool :: Bool -> Expr Bool
  Gte :: Expr Int -> Expr Int -> Expr Bool
  Add :: Expr Int -> Expr Int -> Expr Int
  ColumnName :: String -> Expr a

然后,以下将全部编译:

ColumnName "age" `Gte` LiteralInt 40
LiteralInt 40 `Gte` LiteralInt 10
(LiteralInt 40 `Add` ColumnName "age") `Gte` LiteralInt 10

而以下情况不会:

LiteralInt 40 `Gte` LiteralBool True
LiteralInt 40 `Add` LiteralBool True
  

但是,说“年龄”不包含可以表示为LiteralInt的内容。

如果你在编译时知道你的架构并且想要做很多类型hackery,你可能可能会使这也成为编译时间。更简单的解决方案就是在执行查询时进行错误处理。所以你的功能看起来像

evalExpr :: Expr a -> ExceptT e IO a

你可能会在这里对列的类型进行适当的检查。