【问题标题】:Does Haskell have variadic functions/tuples?Haskell 有可变参数函数/元组吗?
【发布时间】:2011-11-05 11:07:48
【问题描述】:

uncurry 函数仅适用于采用两个参数的函数:

uncurry :: (a -> b -> c) -> (a, b) -> c

如果我想对具有任意数量参数的函数进行 uncurry,我可以编写单独的函数:

uncurry2 f (a, b)          = f a b
uncurry3 f (a, b, c)       = f a b c
uncurry4 f (a, b, c, d)    = f a b c d
uncurry5 f (a, b, c, d, e) = f a b c d e

但这很快就会变得乏味。有什么方法可以概括这个,所以我只需要写一个函数吗?

【问题讨论】:

  • 不是那样,但无论如何,你为什么要这样做?根据我的经验,在极少数情况下您需要的不仅仅是 uncurry2
  • @Paul:没有具体的原因,但是当我看到任何一种重复的模式时,我就会问自己如何概括和抽象出来。

标签: function haskell tuples currying variadic


【解决方案1】:

tuple 包中尝试uncurryN。像所有形式的重载一样,它是使用类型类实现的。在这种情况下,通过手动拼出最多 15 个元组的实例,这应该绰绰有余了。

变量函数也可以使用类型类。这方面的一个例子是Text.Printf。在这种情况下,它是通过对函数类型的结构归纳来完成的。简单来说,它的工作原理是这样的:

class Foo t

instance Foo (IO a)
instance Foo b => Foo (a -> b)

foo :: Foo

不难看出foo 可以实例化为IO aa -> IO ba -> b -> IO c 等类型。 QuickCheck 也使用了这种技术。

不过,结构归纳不适用于元组,因为 n-tuple 与 n+1-tuple 完全无关,所以这就是实例具有需要手动拼写。

【讨论】:

  • 有趣的是,Data.Tuple.Curry 的来源提到,不同的实例实际上是由一个单独的程序生成的,然后可能像手工输入一样粘贴进去。
【解决方案2】:

找到使用过度类型系统技巧来伪造这种东西的方法是我的爱好之一,所以当我说结果非常丑陋时请相信我。特别要注意,元组不是递归定义的,因此没有真正的方法可以直接抽象它们;就 Haskell 的类型系统而言,每个元组的大小都是完全不同的。

因此,任何直接处理元组的可行方法都需要代码生成——使用 TH 或使用 tuple 包的外部工具。

要在不使用生成代码的情况下伪造它,您必须首先使用递归定义——通常是带有“nil”值的右嵌套对来标记结束,(,)() 或等同于他们。你可能会注意到这类似于(:)[] 对列表的定义——事实上,这种递归定义的伪元组可以被视为类型级别的数据结构(列表类型)或异构列表(例如,HList 以这种方式工作)。

缺点包括但不限于,实际上使用以这种方式构建的东西可能比它的价值更尴尬,实现类型系统技巧的代码通常令人费解且完全不可移植,而且最终结果不一定是相同的——例如,(a, (b, (c, ())))(a, b, c) 之间存在多个重要差异。

如果你想知道它变得多么可怕,你可以看看the stuff I have on GitHub,尤其是here

【讨论】:

    【解决方案3】:

    没有直接的方法可以编写适用于不同数量参数的uncurry 的单一定义。

    但是,可以使用Template Haskell 生成许多不同的变体,否则您必须手动编写这些变体。

    【讨论】:

    • 您可以使用 Daniel Fridlender 和 Mia Indrika 的 arity 系列函数模式编写一个 n-ary uncurryzipWithN 的例子在论文“我们需要依赖类型吗?”中给出。
    • “没办法”可能有点大胆。我已将答案改为“没有直截了当的方法”。
    • @stephentetley 该实现是否已在任何地方的包中实现?
    猜你喜欢
    • 1970-01-01
    • 2021-01-12
    • 1970-01-01
    • 1970-01-01
    • 2018-02-15
    • 1970-01-01
    • 1970-01-01
    • 2015-01-05
    • 1970-01-01
    相关资源
    最近更新 更多