如何创建一个polyvariadic haskell函数? [英] How to create a polyvariadic haskell function?
问题描述
我需要一个接受任意数量参数(全部为相同类型)的函数,对它们做一些事情并在事后给出结果。在我的具体情况下,参数列表是不切实际的。
当我查看haskell库时,我发现函数 printf
(来自模块 Text.Printf
)使用类似的技巧。不幸的是,我无法通过查看源代码来理解这种魔法。
有人可以解释如何实现这一点,或者至少是某些网页/纸张/找到一个很好的描述?
动机:
这真的很简单。对于学校(计算机科学课程),我们需要编写一个能够记录数学表达式的模块,将其表达为一个字符串(通过为自己的数据类型编写一个Num / Real / etc实例),并执行对其进行各种操作。
这个数据类型包含一个变量的特殊构造函数,它可以被一个值或任何指定函数替代。其中一个目标是编写一个函数,该函数使用一些变量(类型为(Char,Rational)
)的变量,并计算表达式的结果。我们应该看看如何最好地表达功能的目标。 (我的想法:函数返回的函数与函数中定义的变量一样多 - 看来是不可能的)。
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)=>字符串 - > r
printf fmts = spr fmts []
class PrintfType t其中
spr :: String - > [UPrintf] - > t
实例(IsChar c)=> PrintfType [c]其中
spr fmts args = map fromChar(uprintf fmts(反向参数))
实例(PrintfArg a,PrintfType r)=> PrintfType(a - > r)其中
spr fmts args = \ a - > spr fmts(toUPrintf a:args)
我们可以提取的基本结构是
variadicFunction :: VariadicReturnClass r => RequiredArgs - > r
variadicFunction reqArgs = variadicImpl reqArgs mempty
class VariadicReturnClass r其中
variadicImpl :: RequiredArgs - > AccumulatingType - > r
instance VariadicReturnClass ActualReturnType其中
variadicImpl reqArgs acc = constructActualResult reqArgs acc
实例(ArgClass a,VariadicReturnClass r)=> VariadicReturnClass(a - > r)其中
variadicImpl reqArgs acc = \ a - >例如:
class SumRes r其中
sumOf :: Integer - > r
实例SumRes整数其中
sumOf = id
实例(Integral a,SumRes r)=> SumRes(a - > r)其中
sumOf x = sumOf。 (x +)。 toInteger
然后我们可以使用
*主> 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 ::整数
59
I need a function which takes an arbitrary number of arguments (All of the same type), does something with them and afterwards gives a result back. A list of arguments is impracticable in my specific case.
As I looked through the haskell libs, I saw that the function printf
(from module Text.Printf
) uses a similar trick. Unfortunately, I couldn't understand that magic by looking at the source.
Can somebody explain how to achieve this, or at least some webpage/paper/whatever where I could find a good description for this?
Motivation:
The reason I need this is really quite simple. For school (computer science class), we are required to write a module that is able to "record" a mathematical expression, express it as a string (Via writing an instance of Num/Real/etc for an own datatype), and perform various operations on it.
This datatype contains a special constructor for a variable, which may be replaced by a value or whatever by a specified function. One of the goals is to write a function, which takes such an expression with some number of variables (pairs of type (Char,Rational)
) and calculates the result of the expression. We should look at how to express the goal of the function best. (My idea: The function returns another function which takes exactly as many arguments as vars that are defined in the function - seems to be impossible).
解决方案 The key points of printf
is the ability to either return a String or a function. Copied from 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)
and the basic structure we can extract out is
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)
For instance:
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
then we could use
*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
这篇关于如何创建一个polyvariadic haskell函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!