Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing list elements as parameters to curried function

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?

like image 947
Ishpeck Avatar asked Sep 23 '10 04:09

Ishpeck


1 Answers

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.

like image 154
John L Avatar answered Oct 02 '22 14:10

John L