如何解决我的 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 Selection
s 和 Reduction
s
允许执行聚合的查询。
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
的类型强制这样做是这样的。
我有两个 GADT 用来模拟 SQL EDSL。为了让面向 api 的客户端保持干净和简单,我想使用 OverloadedStrings
将字符串文字转换为 Column Selection
。
因此您只需输入
select ["a", "b"] $ from tbl
而不是
select [Column "a", Column "b"] $ from tbl
问题是 select 允许 Column Selection
s 和 Reduction
s
允许执行聚合的查询。
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
的类型强制这样做是这样的。