如何采用类解析器模式实现Hasql查询结果到嵌套记录的可组合映射
问题1解答
你提到的Applicative组合式结果解析方案是同类场景下非常成熟的常用方案,没有遗漏明显的优化点。
这类场景的本质就是将SQL查询返回的扁平行数据,按约定顺序反序列化为嵌套的业务数据类型,几乎所有SQL库的行解码器底层都是基于类似的思路实现:
- 优势非常明显:各层级的解析逻辑完全独立可复用,增删字段、调整结构时只需要修改对应层级的解析函数,不需要重构整个巨型元组的解构逻辑,也能避免元组字段顺序数错的低级错误。
- 如果你不想自己实现这套逻辑,也可以选择直接放弃Hasql-TH的自动元组生成,改用Hasql原生的
Row解码器接口,它本身就内置了Applicative实例,支持直接组合出嵌套类型的解码器,连手动转换元组的步骤都可以省掉。 - 额外补充可选方案:如果嵌套层级非常深、结构变化频繁,也可以考虑在SQL侧用
json_build_object、json_agg这类JSON聚合函数直接返回嵌套JSON结构,再通过Aeson反序列化到业务类型,缺点是损失了SQL层面的字段类型检查,适合快速迭代的场景。
问题2解答
完全不需要改造Megaparsec,自行实现极简的Applicative/Monad解析器是成本最低的方案。
Megaparsec本身是面向通用流解析设计的,内置了大量你不需要的回溯、错误定位、字符匹配等能力,引入进来反而会增加不必要的依赖和运行开销。你需要的解码器逻辑非常简单,本质就是一个带剩余值列表状态的错误处理计算,几十行代码就能实现核心能力:
-- 假设你用Data.Dynamic或者自定义的统一行值类型承载元组元素,也可以用Generics直接处理元组 newtype RowDecoder a = RowDecoder { runDecoder :: [Dynamic] -> Either String (a, [Dynamic]) } instance Functor RowDecoder where fmap f (RowDecoder d) = RowDecoder $ \s -> do (res, s') <- d s Right (f res, s') instance Applicative RowDecoder where pure x = RowDecoder $ \s -> Right (x, s) (RowDecoder f) <*> (RowDecoder x) = RowDecoder $ \s -> do (f', s1) <- f s (x', s2) <- x s1 Right (f' x', s2) -- 基础原语:取出下一个值做类型转换 takeColumn :: Typeable a => RowDecoder a takeColumn = RowDecoder $ \case [] -> Left "Unexpected end of row values" (x:xs) -> case fromDynamic x of Just v -> Right (v, xs) Nothing -> Left "Type mismatch when decoding column"
实现之后你就可以按业务层级拆分独立的解码器,通过<$>/<*>直接组合:
countryDecoder :: RowDecoder Country countryDecoder = Country <$> takeColumn <*> takeColumn townDecoder :: RowDecoder Town townDecoder = Town <$> takeColumn <*> takeColumn <*> countryDecoder authorDecoder :: RowDecoder Author authorDecoder = Author <$> takeColumn <*> takeColumn <*> townDecoder genreDecoder :: RowDecoder Genre genreDecoder = Genre <$> takeColumn bookDecoder :: RowDecoder Book bookDecoder = Book <$> takeColumn <*> takeColumn <*> genreDecoder <*> authorDecoder
原来的mkBook只需要把TH返回的大元组转成元素列表传入解码器运行即可:
mkBook rowTuple = either error fst $ runDecoder bookDecoder (tupleToList rowTuple)
内容的提问来源于stack exchange,提问作者GTF
相关产品推荐
相关产品推荐

