3

I'm currently learning Haskell and I must say, I am having a terrible time.

I've been tasked with the exercise of creating a function, evens, which takes a value, x, and returns a list of all even values from 0 to x.

For example:

>  evens 10

> [2,4,6,8,10]

I have been attempting to modify some example functions using list comprehension to achieve my goal however I have simply been riddled with errors, worse even I've tried so many things everything is one big confusing blur.

My last attempt went as follows:

evens :: int -> [int]
evens n = [x | x <- [0..n], filter even x]

It produced the error:

ex1.hs:9:29: error:

• Couldn't match expected type ‘Bool’ with actual type ‘[Integer]’

• In the expression: filter even x

In a stmt of a list comprehension: filter even x

In the expression: [x | x <- [0 .. n], filter even x]

I do see its expecting a Boolean but quite frankly I don't understand where or why.

Any help would be largely appreciated. I have never had any experience with functional programming languages and I am having a hard time figuring out my errors in thought. I feel like I've overthought this one simple question to a point beyond belief.

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
  • 3
    A boolean expression in a list comprehension is already treated as a filter. You don't need to say `filter even x`, you just say `even x`. Also the language is case sensitive, make sure you say `Int` instead of `int` in your type signature. – luqui Sep 16 '19 at 06:11
  • 1
    Sorry to hear that. [List comprehensions are fun](https://stackoverflow.com/questions/56215405/custom-filter-function-with-predicate-using-list-comprehension/56215421#56215421). :) – Will Ness Sep 16 '19 at 06:35
  • 1
    Not worth writing an answer for but no-one has told you the nicest solution yet: `evens n = [0,2..n]`. – David Fletcher Sep 16 '19 at 08:45

2 Answers2

7

Just remove the word filter from that code.

The Boolean that is expected is even x. When this value is True, x will be included in the output list.

filter is a list-creating function, but the list comprehension is already creating a list, itself.

In fact, filter p could be coded as

filter p xs = [x | x <- xs, p x]

So using filter on the inside is quite redundant.


About that error message. You say

I do see its expecting a Boolean but quite frankly I don't understand where or why.

But the error message shows us where:

ex1.hs:9:29: error:

• Couldn't match expected type ‘Bool’ with actual type ‘[Integer]’

• In the expression: filter even x

This is where. It is the most immediate culprit: the expression filter even x. It is its actual type, [Integer], that does not match the expected type, Bool.

In a stmt of a list comprehension: filter even x

That expression appears in as a "stmt" statement in our list comprehension,

In the expression: [x | x <- [0 .. n], filter even x]

which is the above full expression. So we just need to read those messages inside-out, to find our way to the (immediate) place of the error.


So from the context of the list comprehension, [ r | x <- xs, q], the q is expected to have the type Bool.

But in itself, q = filter even x has type [Integer], the list of integers.

Why? Because (with the simplified, non-polymorphic types as pertinent to this context)

  filter      :: (Integer -> Bool) -> [Integer] -> [Integer]
         even :: (Integer -> Bool)
  -----------------------------------------------------------
  filter even ::                      [Integer] -> [Integer]
              xs                   :: [Integer]
  -----------------------------------------------------------
  filter even xs                                :: [Integer]

(read :: as "has type"); whereas

  even   :: (Integer -> Bool)
       x ::  Integer
  ---------------------------
  even x             :: Bool

And if you're wondering why it's Integer even though you wrote int, the short answer to that is, int is a type variable just like a or b or t or ... . It is not a type, like Int. So you get the default which, here, is Integer.

Community
  • 1
  • 1
Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • I think there is more going on here. You have used `xs :: [Integer]` but OP has `x :: Int`. And infact `:t filter even (1::Int)` complains: "Couldn't match `[a]` with `Int`, whereas `:t filter even (1::_)` reports `1` to be of type `[a]`. A newcomer might be very confused that the number 1 is accepted by Haskell as a list (of type `[a]`). Maybe one should discuss this topic in a separate question but I want to point that out for future readers. – Micha Wiedenmann Sep 16 '19 at 11:02
  • @MichaWiedenmann uh, I think I understand your comment now. you probably referred to the `x` in `filter even x` in the OP's code, which type is known as `x :: Integer` (or `Int`, whatever) from the context; but when I show the type derivation I derive the type of the expression `filter even xs` *"in itself"*, i.e. just from the type of `filter` and `even` -- to follow the error message. GHC *could* choose your approach, to focus on the `x` instead of `filter even x`, then say "by LC `x` is known to be `Int`, but `filter even` expects `[a]`" .... – Will Ness Sep 16 '19 at 16:11
  • Yes. (Curiously in `evens :: a -> [a]; evens n = [x | x <- "", filter even x]` the compiler gives a second warning (`[Integer]` doesn't match with `Char`), but it doesn't in ` evens :: a -> [a]; evens n = [x | x <- [0], filter even x]`). – Micha Wiedenmann Sep 16 '19 at 18:23
3

An alternative but also correct formulation would be,

Prelude> filter (\x -> even x) [1..10]
[2,4,6,8,10]

You can also drop the lambda function here and do

Prelude> filter even [1..10]
[2,4,6,8,10]

More succinctly, in the list-comprehension format.

Prelude> [x | x <- [1..10], even x]
[2,4,6,8,10]
Skam
  • 7,298
  • 4
  • 22
  • 31
  • `\x -> even x` is equivalent to `even` in this context. – chepner Sep 16 '19 at 21:06
  • 2
    yes, that is an equivalent function but for a haskell noobie, I think it's a little more clear what is going on when you express it as a lambda – Skam Sep 16 '19 at 21:33
  • If anything, I think it implies a difference between a function defined by the expression and the original function that simply doesn't exist. – chepner Sep 16 '19 at 21:37