如何解决我的GADT中的歧义

时间:2016-03-02 20:11:19

标签: haskell types dsl gadt

我有两个用于建模SQL EDSL的GADT。为了让客户端面对api干净简单,我想使用OverloadedStrings将字符串文字转换为Column Selection

因此您只需输入

即可
select ["a", "b"] $ from tbl

而不是

select [Column "a", Column "b"] $ from tbl

问题是select允许Column SelectionReduction 允许执行聚合的查询。

mean :: Column Selection -> Column Reduction

select :: [Column a] -> Query b -> Query Selection
select [mean "a"] $ from tbl

因此在[Column a]的上下文中字符串是不明确的。 但是select [mean "a"] $ from tbl是有效的,因为mean提供了必要的上下文来推断字符串文字是列选择。

任何人都可以推荐摆脱这种混乱的方法吗?

我目前的代码(省略了无关的实例)

{-# LANGUAGE 
    GADTs
  , RankNTypes
  , DataKinds
  , TypeFamilies
  , FlexibleContexts
  , FlexibleInstances
  , OverloadedStrings #-}

data Sz = Selection | Reduction deriving Show
data Schema = Schema{name :: Maybe String, spec :: [Column Selection]} 

type family ColOp (a :: Sz) (b :: Sz) where
    ColOp Selection Selection = Selection
    ColOp Selection Reduction = Selection
    ColOp Reduction Selection = Selection
    ColOp Reduction Reduction = Reduction

data Column (a :: Sz) where
    Column  :: String -> Column Selection
    Assign  :: String -> Column a -> Column a
    FKey    :: String -> Schema -> Column Selection
    BinExpr :: BinOp  -> Column a -> Column b -> Column (ColOp a b)
    LogExpr :: LogOp  -> Column a -> Column b -> Column Selection
    AggExpr :: AggOp  -> Column Selection -> Column Reduction

instance IsString (Column Selection) where
    fromString s = Column s

data Query (a :: Sz) where
    Table  :: Schema -> Query Selection
    Select :: [Column a] -> Query b -> Query Selection
    Update :: [Column a] -> Query b -> Query Selection
    Where  :: [Column Selection] -> Query Selection -> Query Selection
    Group  :: [Column Selection] -> Query Selection -> Query Reduction

我还想对Select / Update进行以下签名失败:

[Column Selection] -> Query Reduction -> Query Selection

但那是另一种蠕虫......

1 个答案:

答案 0 :(得分:6)

编译器是正确的,为Select ["a"]提供了一个模糊的类型错误 - 如果先验IsString (Column Selection)实例只能选择 已知Column的参数为Selection。这正是预期的行为。

您想要的是以下内容:

instance (x ~ Selection) => IsString (Column x) where
    fromString = Column 

这将允许编译器推断出"x" :: Column _必须实际上是"x" :: Column Selection,而不是要求它。

Select [mean "a"]是一个完全不同的情况 - 自mean :: Column Selection -> Column Reduction以来,编译器在实例选择发生之前知道"a" :: Column Selection,因为mean的类型迫使它成为prepared a avia model schema for test. Ticket(uuid: Long, flyCompany: FlyCompany, sitNumber: Int, user: User, date: String, ... ) User(uuid: Long, firstName: String, lastName: String, ... ) 案子。