Sql 具有无标记终结符的静态不变量 TL;DR使用无标记编码时,如何对GADT可能使用的静态不变量进行编码?
我正在一种类似SQL的语言的基础上构建一个dsl,目标是完善一些粗糙的边缘、类型安全性和较小的查询优化 在我的第一个过程中,我使用GADT强制执行目标语言将执行的几个限制,但在大多数情况下,输出是垃圾。即select语句中混合长度的列,如Sql 具有无标记终结符的静态不变量 TL;DR使用无标记编码时,如何对GADT可能使用的静态不变量进行编码?,sql,haskell,types,dsl,Sql,Haskell,Types,Dsl,我正在一种类似SQL的语言的基础上构建一个dsl,目标是完善一些粗糙的边缘、类型安全性和较小的查询优化 在我的第一个过程中,我使用GADT强制执行目标语言将执行的几个限制,但在大多数情况下,输出是垃圾。即select语句中混合长度的列,如 select mean x, x by date, time from tbl 我通过将列标记为选择(向量)或缩减(标量)来防止这些混合选择,因此编译器可以静态地防止这些操作。然而,我从未想过如何防止从groupby中进行选择 下一个需要改进的特性是将类型检
select mean x, x by date, time from tbl
我通过将列标记为选择(向量)或缩减(标量)来防止这些混合选择,因此编译器可以静态地防止这些操作。然而,我从未想过如何防止从groupby中进行选择
下一个需要改进的特性是将类型检查添加到列算术运算中,我的第一个坏主意是添加另一个幻影类型来携带列类型。这不起作用,因为它会破坏我的模式类型,并且我无法再选择混合类型的列。这让我认为,无标记地嵌入列可能是合适的,因为我可以根据上下文获得适当的表示(类型、大小等)。缺点是,这似乎会将我想要编码的所有不变量推迟到运行时。为了说明这一点,这里是GADT和无标记方法中add
的粗略伪代码
-- | GADT
add :: Column ty s1 -> Column ty s2
add a b = BinExpr Add a b
-- | Tagless
add :: Column c => c -> c -> c
add a b | (a :: ColType) == (b :: ColType) = binexpr Add a b
| TypeMismatch a b
GADT方法的另一个好处是能够指定numty=>…
,以防止在字符串列上添加内容。这类问题似乎会在我的所有代码中传播,因为我现在必须推迟运行时检查,以确保防止选择大小不一的或组
仅处理查询选择
。所以我的问题是:在使用无标记样式的灵活性时,我是否能够以某种方式保留GADT的静态保证
我当前的GADT
{-# LANGUAGE
GADTs
, DataKinds
, TypeFamilies #-}
-- Kinds
-- | State Tags
-- These constructors are promoted to the kind level to be used as
-- phantom types to enforce static query invariants. For instance
-- these are strategically used to prevent selecting from a Groupby
-- or preventing selection of mixlengthed columns
data Sz = Selection | Reduction deriving Show
--------------------------------------------------------------------
-- Schema
data Schema = Schema {
name :: Maybe String,
spec :: [Column Selection]
} deriving Eq
--------------------------------------------------------------------
-- Column Datatype
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
type family ColOp (a :: Sz) (b :: Sz) where
-- | If both sides a scalar then output
-- is still a scalar, otherwise return
-- a vector, as we can perform the op
-- elemwise or brodcast a scalar across
-- the vector
ColOp Reduction Reduction = Reduction
ColOp a b = Selection
--------------------------------------------------------------------
-- Query types
class Tabular repr where
meta :: repr (a :: Sz) -> Schema
data Query (a :: Sz) where
Table :: Schema -> Query Selection
Select :: (Tabular t, Show (t b)) => [Column a] -> t b -> Query Selection
Where :: (Tabular t, Show (t Selection)) => [Column Selection] -> t Selection -> Query Selection
Group :: (Tabular t, Show (t Selection)) => [Column Selection] -> t Selection -> Query Reduction
无标记列原型
{-# language FlexibleInstances #-}
data Sz = Selection | Reduction
deriving Show
data ColType
= Int | Float | String | Bool
| TypeMismatch ColType ColType
deriving (Show, Eq)
data BinOp = Add | Mul
data AggOp = Mean | Count
-- Tagless AST
class Show repr => Column repr where
column :: String -> ColType -> repr
assign :: String -> repr -> repr
binExpr :: BinOp -> repr -> repr -> repr
aggExpr :: AggOp -> repr -> repr
-- Show Instances
instance Show BinOp where
show Add = " + "
show Mul = " * "
instance Show AggOp where
show Mean = "mean "
show Count = "count "
-- I Would like to bring these into the type level (ie performed statically)
opSz :: Sz -> Sz -> Sz
opSz Reduction Reduction = Reduction
opSz a b = Selection
typeCheck :: ColType -> ColType -> ColType
typeCheck a b | a == b = a
| otherwise = TypeMismatch a b
-- Tagless Interpreters
-- | Stringify
instance Column ([Char]) where
column s t = s
assign s c = s ++ ": " ++ show c
binExpr o l r = l ++ show o ++ r
aggExpr op l = show op ++ l
-- | Column Size
instance Column Sz where
column s t = Selection
assign s c = c :: Sz
binExpr o l r = opSz l r
aggExpr op l = Reduction
-- | Column Type
instance Column ColType where
column s t = t
assign s c = c :: ColType
binExpr o l r = typeCheck l r
aggExpr o l = l :: ColType
add :: Column c => c -> c -> c
add l r = binExpr Add l r
columns :: Column c => [c]
columns = [ column "a" Float
, column "b" Int
, column "c" String ]
a :: Column c => c
a = columns !! 0
b :: Column c => c
b = columns !! 1
-- add a b :: ColType => TypeMismatch Float Int
Edit:我可以通过向类中添加类型参数来获得类似的行为
class Column repr where
column :: String -> repr Selection
assign :: String -> repr (a :: Sz) -> repr (a :: Sz)
binExpr :: BinOp -> repr (a :: Sz) -> repr (b :: Sz) -> repr (OpSz a b)
aggExpr :: AggOp -> repr Selection -> repr Reduction
但我仍然很好奇如何定义ast的解释器,将ColType
带到类型级别,这样我就可以定义add::ct->ct->ct
所以adda::(C字符串)b:(C Int)
首先,在Haskell中正确地键入数据库似乎相当痛苦。它不太适合Haskell的类型系统。其次,我有限的经验表明,GADT比无标记的最终编码更容易思考。即使您想要后者,也可以更容易地从前者和transform.Oleg开始。基本上,将GADT构造函数模拟为类型类的方法,包括类型参数。我不确定为什么列
GADT被允许是Sz->*
类型,但是列
类对类型进行分类*
。我当然可以这样做(参见编辑)。不过,我想我想要的是一个无标记ast的解释器,它可以在方便的时候将列类型(Int、Float等)提升到类型级别,这样我就可以用签名定义add,比如add::CT->CT->CT