如何在Haskell中为自定义HINQ数据类型实现Semigroup类型类实例
HINQ类型的Semigroup实例实现方案
我们的实现核心对齐SQL的UNION语义,即两个HINQ查询合并后返回的结果等价于两个查询各自执行后结果的拼接,满足Semigroup要求的结合律。
首先先定义HINQ查询的执行函数方便理解逻辑:
runHINQ :: HINQ m a b -> m b runHINQ (HINQ select src whereCond) = select (whereCond src) runHINQ (HINQ_ select src) = select src
Semigroup实例实现
我们要求合并后查询的执行结果满足 runHINQ (q1 <> q2) = runHINQ q1 <> runHINQ q2,基于该规则实现所有组合分支:
instance (Semigroup (m a), Semigroup (m b)) => Semigroup (HINQ m a b) where -- 两个带WHERE的查询合并 (HINQ sel1 src1 cond1) <> (HINQ sel2 src2 cond2) = HINQ (\_ -> sel1 (cond1 src1) <> sel2 (cond2 src2)) (src1 <> src2) id -- 带WHERE查询和无WHERE查询合并 (HINQ sel1 src1 cond1) <> (HINQ_ sel2 src2) = HINQ (\_ -> sel1 (cond1 src1) <> sel2 src2) (src1 <> src2) id -- 无WHERE查询和带WHERE查询合并 (HINQ_ sel1 src1) <> (HINQ sel2 src2 cond2) = HINQ (\_ -> sel1 src1 <> sel2 (cond2 src2)) (src1 <> src2) id -- 两个无WHERE查询合并 (HINQ_ sel1 src1) <> (HINQ_ sel2 src2) = HINQ_ (\_ -> sel1 src1 <> sel2 src2) (src1 <> src2)
实现说明
- 源表合并:直接使用
Semigroup的<>操作合并两个查询的源表,若m为列表类型对应SQL的UNION ALL,若m为带去重能力的上下文对应UNION - 筛选逻辑处理:两个查询各自的WHERE条件仅作用于自身的原始源表,合并后的查询额外筛选逻辑设为
id(即无额外筛选) - 投影逻辑处理:合并后的SELECT逻辑为分别执行两个原始查询的投影+筛选流程,再合并返回结果,完全不需要修改原始类型签名,不会出现你之前尝试转列表导致的类型变化问题
配套Monoid实例实现
如果需要实现Monoid,单位元设为返回空结果的空查询即可:
instance (Monoid (m a), Monoid (m b)) => Monoid (HINQ m a b) where mempty = HINQ_ (\_ -> mempty) mempty
该实现完全满足Monoid的单位元规则:runHINQ (mempty <> q) = runHINQ q = runHINQ (q <> mempty)。
内容的提问来源于stack exchange,提问作者A. G
相关产品推荐
相关产品推荐

