如何创build一个polyvariadic haskell函数?

我需要一个函数,它接受任意数量的参数(全部是相同的types),对它们做一些事情,然后给出结果。 在我的具体情况下,争议清单是不切实际的。

当我查看haskell库时,我发现printf (来自模块Text.Printf )使用了类似的技巧。 不幸的是,我无法通过查看源代码来理解这种魔法。

有人可以解释如何实现这一点,或者至less有一些网页/纸/无论我可以find一个很好的描述呢?

动机:

我需要这个的原因真的很简单。 对于学校(计算机科学类),我们需要编写一个能够“logging”mathexpression式的模块,将其expression为一个string(通过为自己的数据types编写一个Num / Real / etc实例),并执行它的各种操作。

这个数据types包含一个variables的特殊构造函数,它可以被一个指定的函数replace为一个值或任何东西。 其中一个目标是编写一个函数,该函数使用一些variables(types对(Char,Rational) )来计算expression式的结果。 我们应该看看如何最好地expressionfunction的目标。 (我的想法:该函数返回另一个函数,该函数与函数中定义的variables一样多的参数 – 似乎是不可能的)。

printf的关键是能够返回一个String或一个函数。 复制自http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/Text-Printf.html

 printf :: (PrintfType r) => String -> r printf fmts = spr fmts [] class PrintfType t where spr :: String -> [UPrintf] -> t instance (IsChar c) => PrintfType [c] where spr fmts args = map fromChar (uprintf fmts (reverse args)) instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where spr fmts args = \a -> spr fmts (toUPrintf a : args) 

我们可以提取出来的基本结构是

 variadicFunction :: VariadicReturnClass r => RequiredArgs -> r variadicFunction reqArgs = variadicImpl reqArgs mempty class VariadicReturnClass r where variadicImpl :: RequiredArgs -> AccumulatingType -> r instance VariadicReturnClass ActualReturnType where variadicImpl reqArgs acc = constructActualResult reqArgs acc instance (ArgClass a, VariadicReturnClass r) => VariadicReturnClass (a -> r) where variadicImpl reqArgs acc = \a -> variadicImpl reqArgs (specialize a `mappend` acc) 

例如:

 class SumRes r where sumOf :: Integer -> r instance SumRes Integer where sumOf = id instance (Integral a, SumRes r) => SumRes (a -> r) where sumOf x = sumOf . (x +) . toInteger 

那么我们可以使用

 *Main> sumOf 1 :: Integer 1 *Main> sumOf 1 4 7 10 :: Integer 22 *Main> sumOf 1 4 7 10 0 0 :: Integer 22 *Main> sumOf 1 4 7 10 2 5 8 22 :: Integer 59 

很多人告诉你如何创build可变参数函数,但是我认为在这种情况下,使用[(Char,Rational)]types的列表实际上更好。

在关于variadic函数的wiki文章中,引用了这篇文章 。 我想这是printf做的,但我也不明白。 无论如何,这当然是一个矫枉过正,尤其是因为你的论点都是相同的types。 把它们放在一个列表中。 这就是列表所擅长的 – 任意数量的相同types的东西。 好吧,它不是很漂亮,但它不会比完整的多元变化函数更丑。

我看了一个从delnan引用的文章链接的例子 。 在盯着它之后,我想我终于明白了发生了什么事情:

它从这个types开始:

 class BuildList ar | r-> a where build' :: [a] -> a -> r 

pipe道(|)之后的那一点是一个函数依赖。 它表示由a表示的types可以由r表示的types确定。 换句话说,你不能用同样的r (返回types)定义BuildListtypes类的两个实例,但是不同的a

向前跳一点到build'函数实际使用的地方:

 > build True :: [Bool] 

由于build只是作为第一个参数使用空列表调用build' ,这是一样的:

 > build' [] True :: [Bool] 

在这个例子中, build'显然返回一个列表。 由于函数依赖关系,我们只能绑定到这个BuildListtypes的实例:

 instance BuildList a [a] where build' lx = reverse$ x:l 

非常简单 第二个例子更有趣。 扩展build的定义,它变成:

 > build' [] True False :: [Bool] 

在这种情况下build'的types是什么? 那么,Haskell的优先规则意味着以上也可以这样写:

 > (build' [] True) False :: [Bool] 

现在很清楚,我们传递两个参数来build'然后将该expression式的结果应用到值为'False'的参数。 换句话说,expression式(build' [] True)有望返回一个Bool -> [Bool]types的函数 Bool -> [Bool]BuildList把我们绑定到了BuildListtypes的第二个实例:

 instance BuildList ar => BuildList a (a->r) where build' lxy = build'(x:l) y 

在这个调用中, l = []x = Truey = False ,所以定义扩展为build' [True] False :: [Bool] 。 这个签名绑定到build'的第一个实例上,而且从那里出发的地方相当明显。

KennyTM的答案很棒。 下面是sumOf 1 4 7 10 :: Integer的exec过程的一个例子,给出一个更好的例子。

 sumOf 1 4 7 10 (( \ x -> ( sumOf . (x +) . toInteger ) 1 ) 4 7 10 ((sumOf . (1 + ) . toInteger) 4 ) 7 10 ( sumOf 5 ) 7 10 ( sumOf . (5 + ) . toInteger ) 7 10 sumOf 12 10 sumOf . (12 + ) . toInteger 10 sumof 22 id 22 22 
Interesting Posts