3

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?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Tom coates
  • 53
  • 1
  • 7
  • 1
    It's unclear what you mean, please add some code/pseudocode explaining _how you would like to use this_. – leftaroundabout Oct 08 '17 at 15:40
  • to your edit: the first part of my answer fully applies. that is, if you use the same int, like `theFunction x i = x < i` and then `filter (flip theFunction 5) aList`, keeping in the resulting list all elements of `aList` that are smaller than 5. – Will Ness Oct 08 '17 at 16:14
  • Seems like this is not a filtering but a folding job. You may try `(==) <$> foldr1 (\x y -> bool (minBound :: Int) x (x <= y)) <*> head`. Where `bool` is a ternary operator from `Data.Bool.bool` with type `a -> a -> Bool -> a`. – Redu Oct 08 '17 at 18:15
  • @Redu [consider](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Bool.html#v:bool) `[1,2,3,1]`: `3 <= 2` is false, so `minBound` will be produced - why?? --- besides, `foldr1`'s implementation (both [old](https://hackage.haskell.org/package/base-4.10.0.0/docs/src/GHC.List.html#foldr1) and [new](https://hackage.haskell.org/package/base-4.10.0.0/docs/src/Data.Foldable.html#line-205)) guarantees the reducing function to be called only with existing values. and [`minimum`](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Foldable.html#v:minimum) already exists. – Will Ness Oct 08 '17 at 22:28
  • @Will Ness I think unlike `reduceRight` in JS, in `\ x y ->`, `x` is the previous element from left. That's why `[1,2,3,4 ]` just resolves `True`. Am i missing something..? – Redu Oct 08 '17 at 22:37
  • @Redu I'm sorry, I should've written `3 <= 1` is false. `foldr1 g [1,2,3,1] == g 1 (g 2 (g 3 1))`. – Will Ness Oct 08 '17 at 22:40
  • @Will Ness Honestly... my interpretation of the question was to check simply if the list is in ascending order. May be i am totally off. – Redu Oct 08 '17 at 22:46
  • 1
    @Redu yet for `[minBound, 3, 1]` your function will return ... `True`. --- I read the question as more about the general use patterns. --- `ascending = and . (zipWith (<=) <*> drop 1)`. – Will Ness Oct 08 '17 at 22:48
  • @Will Ness That's indeed true as an edge case. It's better be done by `foldr` and an initial like `(True, maxBound)`. That `zipWith (<=)` applicative version is beautiful. – Redu Oct 08 '17 at 22:54
  • @Redu any use of bounds like that is a code smell. – Will Ness Oct 08 '17 at 22:58
  • @Will Ness Totally agreed. – Redu Oct 08 '17 at 22:59

1 Answers1

2

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.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • 1
    Perhaps it would be more effective, didactically, to use a lambda expression rather than `flip`. – dfeuer Oct 08 '17 at 16:20
  • @dfeuer I like combinatory definitions more. `\ ->` look like more heavy syntax to get lost in. – Will Ness Oct 08 '17 at 16:20
  • I like neither, I would write this as ``filter (`theFunction`i) aList``. (But preferrable would of course be if `theFunction` were defined in flipped form in the first place.) – leftaroundabout Oct 08 '17 at 18:34
  • @leftaroundabout me too (the ``(`op` v)`` stuff). I meant for the exposition, here, because both the other two options involve new *syntax* that needs to be explained to / processed by a learner. `theFunction` is presumably something that the OP *already* has, a.o.t. defining it especially for this use. – Will Ness Oct 08 '17 at 20:35
  • Good answer. Partial function application is a very powerful technique unfamiliar to many non-Haskell hackers and has a very sweet syntax in Haskell. My only additional suggestion is that if theFunction is really just as simple as suggested by the original post, it would be even clearer when not even given a name. Just plain ‘filter ( – CarlEdman Oct 08 '17 at 20:44
  • @CarlEdman thank you. re: your suggestion, this *is* mentioned in the end of first part (just above the divider line). – Will Ness Oct 08 '17 at 20:45
  • The combinatorial approach is perfectly fine if you just want to make a very local change, like `flip`, or if you can use something that scales fairly well, like the applicative idiom. But if you need to shuffle more arguments, or duplicate in a more controlled way, you'll surely want lambdas at some point to prevent the code from turning into spaghetti. May I ask why you prefer to start teaching the combinatorial approach? – dfeuer Oct 10 '17 at 17:54
  • @dfeuer as I said in one of the above comments, it is immediately understandable without needing to know any additional syntax like that of lambdas or sections. So seems like a good fit for the first steps in learning Haskell. Of course what you said is absolutely valid and very soon lambda expressions are found out to be saving us creating a great number of unnecessary temporary functions just for shuffling some arguments. But also, a minimalist approach has its definite appeal, i.e., less distractions to overload the mind of a reader with. As always, it's a balancing act. – Will Ness Oct 10 '17 at 18:31
  • @dfeuer overall, combinators are more on the pointfree side, and pointfree certainly is nice when appropriate. More algebraic code is easier to follow and reshuffle, a la Bird's program derivations. – Will Ness Oct 10 '17 at 18:35
  • @WillNess, can you point to a particular Bird reference I should see? – dfeuer Oct 10 '17 at 18:37
  • 1
    @dfeuer https://www.amazon.com/Pearls-Functional-Algorithm-Design-Richard/dp/0521513383/ref=asap_bc?ie=UTF8 – Will Ness Oct 10 '17 at 18:39