You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell中如何将联合类型列表转换为各构造子对应列表组成的元组

推荐解法

以下提供三种满足全部需求的实现方案,按需选择即可:

方案1:基于第三方库实现(零样板代码,开箱即用)

使用Haskell生态中成熟的generic-data库,它已经封装好了和类型的通用partition逻辑,你只需要给自定义和类型派生Generic实例,一行就能完成实现:

  1. 先在项目的cabal/stack配置中加入generic-data依赖
  2. 业务代码如下:
{-# 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 13:45:04