Search code examples
haskellfilterparameterspredicatepartial-application

Haskell filter function with multiple parameters


I'm trying to learn Haskell and wondered how to filter a given list, with a function that takes multiple parameters, passing each element of the list with other unchanging elements to the function, to create a new list.

I understand that I can do this to use a bool function to filter the list:

newList = filter theFunction aList

but what happens when the theFunction takes other parameters like this:

theFunction -> elementOfAList -> Int -> Bool 

how then could I filter each element of the list, whilst parsing in another element to the function? Any help would be greatly appreciated :)

Edit -> To provide some more information, if I wanted to have a list of integers from [1..10], that get filtered through a function that takes two integers and returns true if the first one is smaller, how could I do that?


Solution

  • In that case you use a partially applied predicate function, like this

    -- theFunction :: elementOfAList -> Int -> Bool       -- "::" means, "is of type"
    newList = filter (flip theFunction i) aList
    

    because

    flip theFunction i x = theFunction x i
    

    by the definition of flip, so flip theFunction has the type Int -> elementOfAList -> Bool:

    flip ::       (a -> b   -> c   ) -> b -> a -> c
    theFunction :: a -> Int -> Bool
    flip theFunction ::               Int -> a -> Bool
    flip theFunction  (i ::  Int)         :: a -> Bool
    

    where i is some Int value defined elsewhere. a is a type variable, i.e. it can be any type, like the type of a list's elements (i.e. for a list aList :: [a] each element has the same type, a).

    For example, with theFunction x i = x < i you could call filter (flip theFunction 5) aList, keeping in the resulting list all the elements of aList that are smaller than 5. Normally this would just be written as filter (< 5) aList, with operator sections (of which (< 5) is one example, absolutely equivalent to the flip theFunction 5).


    The above filtering will use the same Int value i in calling theFunction for every element x of a list aList. If you wanted to recalculate that Int, it is done with another pattern (i.e., higher-order function),

    mapAccumL :: (acc -> x -> (acc, y)) -> acc -> [x] -> (acc, [y])
    

    Suppose you wanted to keep in a list of ints all the elements as they are being found by theFunction. Then you could do it like

    theFunction :: elementOfAList -> Int -> Bool
    foo :: Int -> [Int] -> [Int]
    foo i xs = concat (snd (mapAccumL g i xs))    -- normally written as
            -- concat $ snd $ mapAccumL g i xs     -- or 
            -- concat . snd $ mapAccumL g i xs      -- or even
            -- concat . snd . mapAccumL g i $ xs
      where
      g acc x   -- g :: (acc -> x -> (acc, y))  according to mapAccumL's signature
        | theFunction x acc = (x, [x])   -- include `x` in output, and update the acc
        | otherwise         = (acc, [])  -- keep the accumulated value, and skip this `x`
    

    Because both x and acc are used in the same role (the first element of the tuple) they both must be of same type.