如何生成多态列表以填充Haskell产品类型?
解决方案
要生成目标类型的多态列表,核心问题是Haskell普通列表无法直接容纳秩2多态值,需要用存在类型(Existential Quantification)或GADT封装多态元素,具体步骤如下:
1. 定义多态值的包装类型
首先定义一个包装器,把forall v. c v => m v这种多态值封装成单态类型,让普通列表可以容纳它:
方式一:存在类型
{-# LANGUAGE ExistentialQuantification #-} -- 封装满足约束c v的m v data PolyM c m = forall v. c v => PolyM (m v)
方式二:GADT(更推荐,类型安全性更强)
{-# LANGUAGE GADTs #-} data PolyM c m where PolyM :: c v => m v -> PolyM c m
两种方式效果一致,GADT在模式匹配时能更好地保留类型约束信息。
2. 生成封装后的列表
用map把函数f应用到列表x的每个元素,并包装成PolyM:
y :: [PolyM c m] y = map (\t -> PolyM (f t)) x
对应你的实际场景,用read函数处理RawData列表:
readPolys :: (SomeMonadError m) => [RawData] -> [PolyM CanRead m] readPolys raws = map (\raw -> PolyM (read raw)) raws
3. 适配populateProductType函数
调整populateProductType的参数类型,让它接受封装后的列表[PolyM c f]:
populateProductType :: (Generic a, Applicative f, ...) => [PolyM c f] -> f a populateProductType polys = -- 内部实现时,对每个PolyM fb,直接使用fb即可 -- 因为PolyM已经携带了c b的约束,模式匹配时约束会自动生效 ...
比如遍历polys时,模式匹配PolyM fb,此时fb的类型是c b => f b,正好符合原函数需要的多态值。
原理说明
Haskell的普通列表是单态容器,元素类型必须是具体的单态类型;而forall v. c v => m v是秩2多态类型,无法直接放入普通列表。通过存在类型/GADT封装后,PolyM c m成为一个单态类型,列表可以容纳不同v对应的m v,同时保留了c v的约束。
内容的提问来源于stack exchange,提问作者Clinton
相关产品推荐
相关产品推荐

