views:

121

answers:

4

Still a Haskell newbie here. I know just enough to get myself into trouble with wrong assumptions. If I have the following function...

quadsum w x y z = w+x+y+z

I want a function that can take a list, use each element as a parameter in a specified function like quadsum, and return a curried function for later use.

I've been trying something to the effect of...

magicalFunctionMaker f [] = (f)
magicalFunctionMaker f (x:xs) = magicalFunctionMaker (f x) xs

With the hope of being able to do this...

magicalFunctionMaker (quadsum) [4,3,2]

Getting a curried function like...:

(((quadsum 4) 3) 2)

Or, alternatively, call:

magicalFunctionMaker (quadsum) [4,3,2,1]

Resulting in...

((((quadsum 4) 3) 2) 1)

Is this possible? How misguided am I?

+1  A: 

Not going to work I think. (((quadsum 4) 3) 2) has a different type Intger -> Integer for instance than ((((quadsum 4) 3) 2) 1) which has a type of Integer.

+6  A: 

I think you are misunderstanding the Haskell type system.

First of all, your "quadsum" function is curried already. You can write "quadsum 4 3" and get back a function that expects the 2 and the 1 as extra arguments. When you write "quadsum 4 3 2 1" that is equivalent to "((((quadsum 4) 3) 2) 1)".

In Haskell a list of integers has a different type to an integer or a tuple such as "(4,3,2,1)". Given this, it is rather difficult to understand what you are trying to do. What is supposed to happen if you write this?

magicalFunctionMaker quadsum [5,4,3,2,1]

Your "magicalFunctionMaker" looks rather like "foldl", except that the function you give to foldl only takes two arguments. So you can write:

mySum = foldl (+) 0

This returns a function that takes a list and sums the elements.

(BTW, once you grok this, learn about the difference between foldl and foldr.

Edit:

Having re-read your question, I think you are trying to get:

magicalFunctionMaker quadSum [4,3,2,1] :: Integer
magicalFunctionMaker quadSum [4,3,2] :: Integer -> Integer

This is impossible because the type of magicalFunctionMaker would depend on the length of the list argument, which would imply dynamic typing. As someone said, polyvariadic functions can do something approaching this (although not with a list argument), but that requires quite a few milli-olegs of type hackery.

Paul Johnson
+1  A: 

You can't even list the cases for different length "manually":

mf f [] = f
mf f [x] = f x
mf f [x,y] = f x y

--Occurs check: cannot construct the infinite type: t = t1 -> t
--Probable cause: `f' is applied to too many arguments
--In the expression: f x
--In the definition of `mf': mf f [x] = f x

That means mf can't take a function of arbitrary "arity", you have to decide for one. For the same reason you can't convert an arbitrary list to a tuple: You can't say how many elements the tuple will hold, but the type system needs to know.

There might be a solution by limiting f to a recursive type a = a -> a by using "forall" (see http://www2.tcs.ifi.lmu.de/~abel/fomega/hm.html). However, I couldn't get it working (it seems I have to tell Leksah to use the flag -XRankNTypes somewhere), and that restriction on f would make your function pretty useless.

[edit]

Thinking about, the closest thing to what you want is probably some kind of reduce function. As Paul pointed out, this is similar to foldl, foldr... (but the version below is without "extra argument" and with a homogenous type compared to foldl. Note the missing base case for empty lists)

mf :: (a → a → a) -> [a] -> a
mf f [x] = x
mf f (x:y:xs) = mf f ((f x y) : xs)
Landei
+2  A: 

Paul Johnson's answer pretty much covers it. Just do

quadsum 4 3 2

and the result will be the function you want, with type Integer -> Integer.

But sometimes this isn't good enough. Sometimes you get lists of numbers, you don't know how long the lists are, and you need to apply the elements to your function. This is a bit harder. You can't do:

magicalFunction2 f [] = f
magicalFunction2 f (x1:x2:xs) = f x1 x2

because the results have different types. In the first case the result needs two arguments, and in the second it's a fully-applied function so no more arguments are allowed. In this case, the best thing to do is hold onto the list and your original function until enough arguments are available:

type PAPFunc f a result = Either (f, [a]) result

magicfunc f xs = Left (f,xs)

apply (Left (f,xs)) ys = Left (f,xs++ys)
apply p _              = p

simp2 :: PAPFunc (a->a->b) a b -> PAPFunc (a->a->b) a b
simp2 (Left (f,(x1:x2:xs))) = Right (f x1 x2)
simp2 p = p

now you can do:

Main> let j = magicfunc (+) []
Main> let m = apply j [1]
Main> let n = apply m [2,3]

Main> either (const "unfinished") show $ simp2 m
"unfinished"
Main> either (const "unfinished") show $ simp2 n
"3"

You'll need a separate simplify function for each arity, a problem that's most easily fixed by Template Haskell.

Using lists of arguments (as opposed to an argument of lists) tends to be very awkward in Haskell because the multiple results all have different types, and there's very little support for collections with variable numbers of differently-typed arguments. I've seen three general categories of solutions:

  1. Explicitly code for each case separately (quickly becomes a lot of work).

  2. Template Haskell.

  3. Type system hackery.

My answer mostly deals with trying to make 1 less painful. 2 and 3 are not for the faint of heart.

Edit: It turns out that there are some packages on Hackage that are related to this problem. Using "iteratee":

import qualified Data.Iteratee as It
import Control.Applicative

magic4 f = f <$> It.head <*> It.head <*> It.head <*> It.head

liftedQuadsum = magic4 quadsum
-- liftedQuadsum is an iteratee, which is essentially an accumulating function
-- for a list of data

Main> p <- It.enumChunk (It.Chunk [1]) liftedQuadsum
Main> It.run p
*** Exception: EofException
Main> q <- It.enumChunk (It.Chunk [2,3,4]) p
Main> It.run q
10

But "iteratee" and "enumerator" are likely overkill though.

John
Thank you. This helps clear up a lot.
Ishpeck