如何创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)定义BuildList
types类的两个实例,但是不同的a
。
向前跳一点到build'
函数实际使用的地方:
> build True :: [Bool]
由于build
只是作为第一个参数使用空列表调用build'
,这是一样的:
> build' [] True :: [Bool]
在这个例子中, build'
显然返回一个列表。 由于函数依赖关系,我们只能绑定到这个BuildList
types的实例:
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
把我们绑定到了BuildList
types的第二个实例:
instance BuildList ar => BuildList a (a->r) where build' lxy = build'(x:l) y
在这个调用中, l = []
和x = True
且y = 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