问题
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).
回答1:
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
回答2:
Lots of people are telling you how to create variadic functions, but I think in this case you're actually better off just using a list of type [(Char,Rational)].
回答3:
KennyTM's answer is great. Below is an example of the exec process of sumOf 1 4 7 10 :: Integer
to give a better illustration.
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
回答4:
In the wiki article on variadic functions, this article was referenced. I suppose this is what printf does, but I don't understand it either. Anyway, this is certainly an overkill, especially since your arguments are all of the same type. Just put them all in one list. That's what lists are good for - an arbitary number of things of the same type. Fine, it's not very beautiful, but it will hardly be uglier than a complete polyvariadic function.
回答5:
I took a look at an example linked from the article that delnan referenced. After staring at it for a bit, I think I finally comprehend what is going on:
It starts with this type class:
class BuildList a r | r-> a where
build' :: [a] -> a -> r
That bit after the pipe (|) is a functional dependency. It says that the type represented by a
can be determined by the type represented by r
. In other words, you are prevented from defining two instances of the BuildList
typeclass with the same r
(return type), but different a
.
Jumping ahead a bit to where the build'
function is actually used:
> build True :: [Bool]
Since build
is just calling build'
with an empty list as the first parameter, this is the same as:
> build' [] True :: [Bool]
In this example, build'
is clearly returning a list. Because of the functional dependency, we can only be binding to this instance of the BuildList
type class:
instance BuildList a [a] where
build' l x = reverse$ x:l
Pretty straightforward. The second example is more interesting. Expanding the definition of build
, it becomes:
> build' [] True False :: [Bool]
What's the type of build'
in this case? Well, the precedence rules of Haskell mean that the above could also be written like this:
> (build' [] True) False :: [Bool]
Now it becomes clear that we are passing two parameters to build'
and then applying the result of that expression to a parameter with value 'False'. In other words, the expression (build' [] True)
is expected to return a function of type Bool -> [Bool]
. And that binds us to the second instance of the BuildList
typeclass:
instance BuildList a r => BuildList a (a->r) where
build' l x y = build'(x:l) y
In this invocation, l = []
and x = True
and y = False
, so the definition expands to build' [True] False :: [Bool]
. That signature binds to the first instance of build'
, and it's fairly obvious where it goes from there.
来源:https://stackoverflow.com/questions/3467279/how-to-create-a-polyvariadic-haskell-function