Controlling function's arity
题
I was reading the answer to this question: Haskell: difference between . (dot) and $ (dollar sign) And the reply struck me as odd... What does he mean +
has no input? And then I tried:
((+) 1)
((+) 1 1)
((+) 1 1 1)
Whoops... sad news. But I'm sure I saw functions that can take seemingly arbitrary or a very large number of arguments to believe that someone had defined them in a way a->b->c...->z. There must be some way to handle it! What I'm looking for is something like &rest or &optional in CL.
解决方案
Sure, you can define a variadic addition function, with some typeclass hackery:1
{-# LANGUAGE TypeFamilies #-}
class Add r where
add' :: (Integer -> Integer) -> r
instance Add Integer where
add' k = k 0
instance (n ~ Integer, Add r) => Add (n -> r) where
add' k m = add' (\n -> k (m+n))
add :: (Add r) => r
add = add' id
And so:
GHCi> add 1 2 :: Integer
3
GHCi> add 1 2 3 :: Integer
6
The same trick is used by the standard Text.Printf
module. It's generally avoided for two reasons: one, the types it gives you can be awkward to work with, and you often have to specify an explicit type signature for your use; two, it's really a hack, and should be used rarely, if at all. printf
has to take any number of arguments and be polymorphic, so it can't simply take a list list, but for addition, you could just use sum
.
1 The language extension isn't strictly necessary here, but they make the usage easier (without them, you'd have to explicitly specify the type of each argument in the examples I gave, e.g. add (1 :: Integer) (2 :: Integer) :: Integer
).
其他提示
It's a syntactical trade-off: you can't (in general) have both variable arity functions and nice Haskell-style syntax for function application at the same time. This is because it would make many expressions ambiguous.
Suppose you have a function foo that allows an arity of 1 or 2, and consider the following expression:
foo a b
Should the 1 or 2 argument version of foo be used here? No way for the compiler to know, as it could be the 2 argument version but it could equally be the result of the 1 argument version applied to b.
Hence language designers need to make a choice.
- Haskell opts for nice function application syntax (no parentheses required)
- Lisp opts for variable arity functions (and adds parentheses to remove the ambiguity)