10

I want to define a function that computes the number of elements in a list that satisfy a given predicate:

  number_of_elements :: (a -> Bool) -> [a] -> Int
  number_of_elements f xs = length (filter f xs)

For example:

  number_of_elements (==2) [2,1,54,1,2]

should return 2.

We can write it shorter:

  number_of_elements f = length . filter f

Is it possible to write it without f parameter?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Łukasz Śliwa
  • 600
  • 4
  • 16
  • 3
    What your are looking for is called "Pointfree style". There is a wiki about it here: http://www.haskell.org/haskellwiki/Pointfree . It teaches you all the tricks like the owl: `((.)$(.))` and the dot: `((.).(.))`. I wouldn't personally recommend this style though. – Thomas Ahle Dec 23 '11 at 22:21
  • 6
    I would recommend playing around with it a bit, to see how it works, but using the partially pointfree style `number_of_elements f = length . filter f`. That's the most readable usually. – Daniel Fischer Dec 23 '11 at 22:33
  • 6
    This is a function I would rarely bother defining, because `length (filter f xs)` is, frankly, easier to read than `number_of_elements f xs`. The latter requires me figure out what your function does by either looking up your function definition, documentation or inferring it from the type; while the former is a straightforward combined use of two functions I understand already—and it's also shorter to write! I would only define this as an auxiliary function in a `where` binding, or as an unexported module function—and even then only if it's going to be the argument to other functions. – Luis Casillas Dec 23 '11 at 22:40
  • 1
    sacundim is quite correct. If you *really* want something more than `length (filter f xs)`, I'd suggest defining the `(.:)` combinator in is7s's answer, and using `length .: filter` as needed, but even that is a bit silly. – C. A. McCann Dec 23 '11 at 23:38

3 Answers3

17

Sure it is:

number_of_elements = (length .) . filter
Rotsor
  • 13,655
  • 6
  • 43
  • 57
  • Thanks, and can you explain why `(length . filter)` or `(length $ filter)` won't work? – vikingsteve Dec 04 '15 at 09:54
  • 1
    @vikingsteve, well, `length . fitler` expands to `\x -> length (filter x)`. Here `filter x` is a function and you can't compute length of a function, whatever that would mean. Similarly, `length $ filter` is trying to compute length of the `filter` function. – Rotsor Jan 23 '16 at 19:28
12

I don't think you can get more readable than the what you suggested. However, just for the fun of it you can do this:

numberOfElements = (.) (.) (.) length filter

or

(.:) = (.) . (.)
numberOfElements = length .: filter
is7s
  • 3,500
  • 1
  • 20
  • 41
  • 3
    `(.*)` is usually called `(.:)`; [lambdabot](http://www.haskell.org/haskellwiki/Lambdabot) calls it that, for instance. – ehird Dec 23 '11 at 22:56
  • @C.A.McCann maybe because (.) is one-character shorter? :) – is7s Dec 23 '11 at 23:38
  • I personally prefer `.*` in fact I uploaded a dumb package to hackage (the "composition" package) that defines it under `Data.Composition` – Dan Burton Dec 24 '11 at 02:01
  • A wonderful property of the semantic editor combinator style--`fmap.fmap` or `result.result`--is that it consistently composes. If you want to go three levels deep, just add a third `fmap` or `result` (or a `first`, `second`, etc). In contrast, the `fmap fmap fmap` becomes something quite different when you add or remove `fmap`s. – Conal Dec 24 '11 at 19:50
7

You might like to read about Semantic Editor Combinators. Take the result combinator from there:

result :: (output -> output') -> (input -> output) -> (input -> output')
result = (.)

The result combinator takes a function and applies it to the result of another function. Now, looking at the functions we have:

filter :: (a -> Bool) -> [a] -> [a]
length :: [a] -> Int

Now, length applies to [a]'s; which, it happens, is the result type of functions of the form foo :: [a] -> [a]. So,

result length :: ([a] -> [a]) -> ([a] -> Int)

But the result of filter is exactly an [a] -> [a] function, so we want to apply result length to the result of filter:

number_of_elements = result (result length) filter
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 1
    I'd rewrite that last line to isolate the composed SEC (semantic editor combinator): `number_of_elements = (result.result) length filter`. Read as: compute the number of elements by applying `length` to the result of the result of `filter`. Here `(result.result)` is the editor combinator and `length` is the (semantic) editor. Definitely read the [SEC post](http://conal.net/blog/posts/semantic-editor-combinators). – Conal Dec 24 '11 at 19:40