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

求可实现指定元组提取的Haskell多态函数(可依赖lens包)

当然可以!不管是用原生Haskell还是借助lens包,都能实现你想要的字段提取功能,甚至还能扩展出更灵活的数据操作能力。

原生Haskell的基础实现

其实你要的fn2本质上是把多个提取函数组合起来,从输入数据里取出对应字段并组成元组。原生Haskell里可以直接用简单的多态函数实现:

fn2 :: (a -> b, a -> c) -> a -> (b, c)
fn2 (f, g) x = (f x, g x)

然后用标准的函数组合来实现你的例子:

fn2 (fst, snd . snd) (3, (4, 5))  -- 结果就是 (3, 5)

这个版本是完全多态的——只要两个函数都能作用于输入类型a,就能组合它们的结果返回一个二元组。不过原生方案的局限在于,要支持三元组、更多元的组合,就得手写对应的函数(比如fn3、fn4),或者用类型类来抽象,但会比较繁琐。

借助lens包的优雅实现

lens包为Haskell提供了一套强大的透镜(Lens)系统,完美解决这类字段提取和组合的问题,而且语法更直观。

首先导入必要的模块:

import Control.Lens
import Control.Arrow ((&&&))

lens提供了预定义的透镜(比如_1、_2对应元组的第1、第2个元素),再结合箭头组合子&&&,就能直接实现你要的功能:

(_1 &&& (_2 . _2)) (3, (4, 5)) == (3, 5)  -- 结果为True

这里&&&的作用是把两个透镜(或函数)组合成一个新函数,它会把输入分别传给两个透镜,然后将结果打包成元组。这种方式不仅支持元组,还能无缝扩展到自定义数据类型——比如你定义一个嵌套的自定义类型:

data Person = Person
  { _name    :: String
  , _age     :: Int
  , _address :: (String, String)  -- 地址是“街道+门牌号”的二元组
  } deriving Show

-- 用Template Haskell自动生成对应的透镜
makeLenses ''Person

然后你可以轻松提取嵌套字段的组合:

(name &&& (address . _2)) (Person "Alice" 30 ("Main St", "Apt 4B"))
-- 结果为 ("Alice", "Apt 4B")

基于lens的进一步扩展功能

lens的能力远不止字段提取,它还提供了一套统一的接口来处理各种数据操作:

  • 修改字段:用(.~)操作符直接修改嵌套字段,比如(_2 . _2 .~ 10) (3, (4, 5))会得到(3, (4, 10))
  • 批量遍历修改:用traverse遍历容器里的元素并修改,比如over (traverse . _1) (+1) [(1,2), (3,4)]会得到[(2,2), (4,4)]
  • 折叠计算:用sumOf、lengthOf等函数对嵌套结构做聚合计算,比如sumOf (_2 . traverse) (3, [1,2,3])会得到6
  • 状态管理:在State monad里用透镜修改状态,比如execState (name .= "Bob") alice会把Alice的名字改成Bob

这些功能如果用原生Haskell实现,需要大量重复的样板代码,但lens把它们统一成了简洁、可组合的接口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:56:10