让我们将在某些列上索引的表定义为具有两个类型参数的类型:
data IndexedTable k v = ???
groupBy :: (v -> k) -> IndexedTable k v
-- A table without an index just has an empty key
type Table = IndexedTable ()
k
将是表索引的所有列的(可能是嵌套的)元组。 v
将是表未索引的所有列的(可能是嵌套的)元组。
因此,例如,如果我们有下表
| Id | First Name | Last Name |
|----|------------|-----------|
| 0 | Gabriel | Gonzalez |
| 1 | Oscar | Boykin |
| 2 | Edgar | Codd |
...并且它在第一列上被索引,那么类型将是:
type Id = Int
type FirstName = String
type LastName = String
IndexedTable Int (FirstName, LastName)
但是,如果它在第一列和第二列上被索引,那么类型将是:
IndexedTable (Int, Firstname) LastName
Table
将实现Functor
、Applicative
和Alternative
type 类。换句话说:
instance Functor (IndexedTable k)
instance Applicative (IndexedTable k)
instance Alternative (IndexedTable k)
所以连接将被实现为:
join :: IndexedTable k v1 -> IndexedTable k v2 -> IndexedTable k (v1, v2)
join t1 t2 = liftA2 (,) t1 t2
leftJoin :: IndexedTable k v1 -> IndexedTable k v2 -> IndexedTable k (v1, Maybe v2)
leftJoin t1 t2 = liftA2 (,) t1 (optional t2)
rightJoin :: IndexedTable k v1 -> IndexedTable k v2 -> IndexedTable k (Maybe v1, v2)
rightJoin t1 t2 = liftA2 (,) (optional t1) t2
然后你会有一个单独的类型,我们称之为 a Select
。此类型还将有两个类型参数:
data Select v r = ???
ASelect
会消耗表中的一堆 type 行v
并产生 type 的结果r
。换句话说,我们应该有一个类型的函数:
selectIndexed :: Indexed k v -> Select v r -> r
我们可能定义的一些 exampleSelect
是:
count :: Select v Integer
sum :: Num a => Select a a
product :: Num a => Select a a
max :: Ord a => Select a a
这种Select
类型将实现Applicative
接口,因此我们可以将多个Select
s 组合成一个 s Select
。例如:
liftA2 (,) count sum :: Select Integer (Integer, Integer)
这将类似于此 SQL:
SELECT COUNT(*), SUM(*)
但是,我们的表通常会有多个列,因此我们需要一种方法将 a 集中Select
到单个列上。让我们调用这个函数Focus
:
focus :: Lens' a b -> Select b r -> Select a r
这样我们就可以编写如下内容:
liftA3 (,,) (focus _1 sum) (focus _2 product) (focus _3 max)
:: (Num a, Num b, Ord c)
=> Select (a, b, c) (a, b, c)
所以如果我们想写这样的东西:
SELECT COUNT(*), MAX(firstName) FROM t
这相当于这个 Haskell 代码:
firstName :: Lens' Row String
table :: Table Row
select table (liftA2 (,) count (focus firstName max)) :: (Integer, String)
因此,您可能想知道如何实现Select
和Table
.
Table
我在这篇文章中描述了如何实现:
http://www.haskellforall.com/2014/12/a-very-general-api-for-relational-joins.html
...你可以实现Select
为:
type Select = Control.Foldl.Fold
type focus = Control.Foldl.pretraverse
-- Assuming you define a `Foldable` instance for `IndexedTable`
select t s = Control.Foldl.fold s t
另外,请记住,这些并不是实现Table
和Select
. 它们只是一个简单的实现,可以帮助您入门,您可以根据需要对其进行概括。
从表中选择列怎么样?好吧,你可以定义:
column :: Select a (Table a)
column = Control.Foldl.list
所以如果你想做:
SELECT col FROM t
...你会写:
field :: Lens' Row Field
table :: Table Row
select (focus field column) table :: [Field]
重要的一点是,您可以在 Haskell 中很好地实现关系 API,而无需任何花哨的类型系统扩展。