如何解决我的 GADT 中的歧义

How to resolve ambiguity in my GADTs

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

因此您只需输入

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

而不是

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

问题是 select 允许 Column Selections 和 Reductions 允许执行聚合的查询。

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

但这完全是另一回事...

编译器为您提供 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 的类型强制这样做是这样的。