Haskell中如何将联合类型列表转换为各构造子对应列表组成的元组
推荐解法
以下提供三种满足全部需求的实现方案,按需选择即可:
方案1:基于第三方库实现(零样板代码,开箱即用)
使用Haskell生态中成熟的generic-data库,它已经封装好了和类型的通用partition逻辑,你只需要给自定义和类型派生Generic实例,一行就能完成实现:
- 先在项目的cabal/stack配置中加入
generic-data依赖 - 业务代码如下:
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeApplications #-} import Data.Generic.Data (partitionSum) import GHC.Generics (Generic) data MySum = CaseA A | CaseB B | CaseC C | CaseD D deriving (Generic, Show) data A = A deriving Show data B = B deriving Show data C = C deriving Show data D = D deriving Show -- 仅需一行定义,不需要手写任何遍历逻辑 partitionSum :: [MySum] -> ([A], [B], [C], [D]) partitionSum = Data.Generic.Data.partitionSum @_ @(A, B, C, D) -- 测试输出符合预期 test :: IO () test = print . partitionSum $ [CaseD D, CaseB B, CaseA A, CaseA A, CaseB B, CaseB B]
该方案底层是单遍遍历,完全没有业务侧的样板代码,是最省心的选择。
方案2:基于GHC Generics自实现(无第三方依赖)
如果不想引入额外依赖,可以自己写一套轻量的泛型partition框架,通用逻辑只需要编写一次,后续所有和类型都可以复用:
通用框架代码(仅需写一次)
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleContexts #-} import GHC.Generics class Partition f res where part :: f p -> res -> res instance Partition a as => Partition (M1 i c a) as where part (M1 x) = part x instance (Partition l res, Partition r res) => Partition (l :+: r) res where part (L1 x) = part x part (R1 x) = part x instance Partition (K1 i a) ([a], rest) where part (K1 x) (xs, rest) = (x:xs, rest) instance Partition l r => Partition l (a, r) where part x (a, rest) = (a, part x rest) genericPartitionSum :: (Generic sum, Partition (Rep sum) res) => res -> [sum] -> res genericPartitionSum init = foldr (part . from) init
业务侧代码
data MySum = CaseA A | CaseB B | CaseC C | CaseD D deriving (Generic) -- 仅需一行定义,指定初始空元组即可 partitionSum :: [MySum] -> ([A], [B], [C], [D]) partitionSum = genericPartitionSum ([], [], [], [])
该方案同样是单遍遍历,业务侧仅需要派生Generic加一行函数定义,没有重复样板代码,运行时开销极低。
方案3:基于Template Haskell实现(极致性能)
如果连Generics的极小运行时开销都不想承担,可以编写TH宏在编译期自动生成和你手写的partitionSum2完全一致的优化代码,性能和手写版本没有任何区别,同时不需要手动编写重复的case分支,适合对性能要求极高的场景。
内容的提问来源于stack exchange,提问作者dumbo
相关产品推荐
相关产品推荐

