haskell - 将多态函数应用于两种不同类型的输入
问题描述
考虑这个函数:
doToBoth f x y = (f x, f y)
它在简单的情况下按预期工作:
doToBoth (2 *) 10 15 == (20, 30)
doToBoth head [1,2] [3,4,5] == (1, 3)
然后我尝试了这些:
doToBoth head [1,10,100] "apple"
doToBoth pred 2 'b'
我希望它们都导致(1, 'a')
,但它们只会导致类型错误。问题是推断的类型doToBoth
不够多态:
doToBoth :: (a1 -> a2) -> a1 -> a1 -> (a2, a2)
看到这一点,我尝试添加显式类型签名来修复它:
doToBoth :: (t ~ (i1 -> o1), t ~ (i2 -> o2)) => t -> i1 -> i2 -> (o1, o2)
这个类型签名被接受了,但它并没有解决问题,并且检查发生了什么:t doToBoth
发现它最终得到了一个与原始推断的类型等效的类型:
doToBoth :: (i2 -> o2) -> i2 -> i2 -> (o2, o2)
编写类型签名以使此函数按我想要的方式工作的正确方法是什么?
解决方案
接受多态参数会使您的函数 rank-2 多态。GHC 对此有一个扩展,但只有当你能够以某种方式量化参数必须支持的类型时才能使用它——使用类型构造函数或 -classes。例如,对于列表,您可以编写
{-# LANGUAGE Rank2Types, UnicodeSyntax #-}
doToBoth_listelem :: (∀ x . [x] -> x) -> [a] -> [b] -> (a,b)
doToBoth_listelem f x y = (f x, f y)
> doToBoth_listelem head [1,10,100] "apple"
(1,'a')
这也适用于pred
示例,并且更有用。在这种情况下,您需要对受类约束的参数进行量化Enum
:
doToBoth_enum :: (Enum a, Enum b)
=> (∀ x . Enum x => x -> x) -> a -> b -> (a,b)
doToBoth_enum f x y = (f x, f y)
> doToBoth_enum pred 2 'b'
(1,'a')
我认为,编写它使其自动适用于论点可能需要的任何此类约束是不可能的。可以用一些聪明的类型族和约束类型来近似它,但我怀疑它最终会实际可用。
推荐阅读
- oracle - JdbcSourceConnector - 不是一个有效的月份
- neo4j - Neo4j - 返回具有多个传出关系的节点
- laravel - 手动删除迁移后如何回滚?
- c# - 在 grpc C# 中返回枚举类型
- javascript - 防止客户端无限期地等待服务器响应
- javascript - 反应:刷新页面时 API 调用失败
- mongodb - 我想在猫鼬中按升序找到那些有更多帖子的个人资料
- python - 根据项目列表过滤此数据框的最有效方法是什么?
- reactjs - REACTJS:App.js 中的更改不会影响本地浏览器
- .net - .Net 应用程序在 Linux 上设置标题后不退出