【发布时间】:2021-04-03 01:12:15
【问题描述】:
我正在开发一种应该简单、直观和简洁的编程语言(是的,我知道,我是第一个提出这个目标的人 ;-) )。
我正在考虑的用于简化容器类型使用的功能之一是使容器元素类型的方法在容器类型本身上可用,基本上作为调用map(...) 方法的快捷方式。这个想法是使用多个元素应该与使用单个元素没有什么不同:我可以将add(5) 应用于单个数字或整个数字列表,并且我不必为“one”与“many”场景。
例如(Java 伪代码):
import static java.math.BigInteger.*; // ZERO, ONE, ...
...
// NOTE: BigInteger has an add(BigInteger) method
Stream<BigInteger> numbers = Stream.of(ZERO, ONE, TWO, TEN);
Stream<BigInteger> one2Three11 = numbers.add(ONE); // = 1, 2, 3, 11
// this would be equivalent to: numbers.map(ONE::add)
据我所知,这个概念不仅适用于“容器”类型(流、列表、集合......),而且更普遍地适用于所有具有map 方法的类仿函数类型(例如,可选,状态单子等)。
实现方法可能更接近于编译器提供的语法糖,而不是通过操作实际类型(Stream<BigInteger> 显然没有扩展BigInteger,即使它这样做了“map-add”方法也会有返回 Stream<BigInteger> 而不是 Integer,这与大多数语言的继承规则不兼容)。
对于这样一个提议的功能,我有两个问题:
(1) 提供此类功能的已知注意事项是什么?容器类型和元素类型之间的方法名称冲突是我想到的一个问题(例如,当我在 List<BigInteger> 上调用 add 时,我是要向列表中添加元素还是要添加数字到列表的所有元素?参数类型应该澄清这一点,但它可能会变得棘手)
(2) 是否有任何现有的语言提供这样的功能,如果有,这是如何在后台实现的?我做了一些研究,虽然几乎每一种现代语言都有类似 map 运算符的东西,但我找不到任何一种语言的一对多区别是完全透明的(这让我相信有一些技术我在这里忽略的困难)
注意:我在不支持可变数据的纯函数上下文中查看此问题(不确定这对于回答这些问题是否重要)
【问题讨论】:
-
函子的基本思想不是实现 processing-many-elements-in-a-containter 的概念。这是关于将一个或多个值放入结构中,而没有可能再次将其/它们取回。因此,最重要的是,仿函数对您的代码施加了限制。为什么我们首先要这样做?因为如果我们永远无法检索数据,它可能是不纯的,即不纯计算的结果,而不会损害我们 porgram 的纯领域。
-
如果函数计算可能是一种有效的计算,则没有充分的理由使其隐含,因为隐含的副作用已经贯穿命令式代码。你想要的是明确有效的计算,这正是 FP 的承诺之一。
-
@scriptum 感谢您对函子“关于将一个或多个值放入结构中,而没有可能再次取回它/它们”的评论。虽然它没有直接回答我的问题,但它帮助我理解了以前我无法理解的函子的一个新方面。
标签: collections functional-programming programming-languages functor language-design