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

Haskell中如何泛化both函数以适配元组不同类型元素的自同态应用

问题分析

默认情况下,Haskell类型推断会给both生成类型(a -> b) -> (a, a) -> (b, b),这个类型要求元组的两个元素类型必须相同,且传入的f是单态函数。但你的需求是让both支持不同类型的元组元素,同时兼容id(可作用于任意类型)和concat . replicate 3(仅作用于列表)这类不同泛化程度的多态函数,因此需要更灵活的类型定义。

优化方案

方案一:使用Rank-2类型实现高泛化性(推荐)

通过开启RankNTypes语言扩展,给both定义Rank-2类型,允许传入的f是适配任意类型的多态函数,同时支持不同类型的元组元素。

{-# LANGUAGE RankNTypes #-}

both :: (forall x. x -> f x) -> (a, b) -> (f a, f b)
both f (x, y) = (f x, f y)

foo :: ([Int], [Char])
foo = ([1], "a")

bar :: ([Int], [Char])
bar = both (concat . replicate 3) foo  -- 编译通过,结果: ([1,1,1], "aaa")

-- 支持 both id 的调用
test :: (Int, String)
test = both id (1, "a")  -- 结果: (1, "a")

类型说明

  • forall x. x -> f x:表示f是一个多态函数,能接受任意类型x并返回f x类型的结果。这里的f可以看作类型构造器:
    • 传入id时,f x等价于x本身,因此both id (a, b)直接返回(a, b);
    • 传入concat . replicate 3时,f x等价于[x],因此会对元组的两个列表元素分别执行重复拼接操作。

方案二:显式传入双函数(无需扩展)

如果不想启用语言扩展,可将both定义为接受一对函数的形式,虽然需要重复传入同一个函数,但同样能满足需求:

both :: (a -> c, b -> d) -> (a, b) -> (c, d)
both (f, g) (x, y) = (f x, g y)

bar :: ([Int], [Char])
bar = both (concat . replicate 3, concat . replicate 3) foo

内容的提问来源于stack exchange,提问作者shooqie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:58:10