Using filter with custom datatypes

Viewed 83

I have these datatypes:

data Command = Back Int | Front Val deriving (Show,Eq)
data Val = Val {first::Int, second::Int, third::Int} deriving (Show, Eq)
type Program = [Command]

I have this function:

foo :: Program -> Int
foo list = length (filter (==Front Val {first, second, third}) list)

The purpose is to find how many times a Front occurs USING FILTER ONLY, and foo gives compilation error. I am not sure how to represent the integer/val part of Front.

2 Answers

You can not use an equality check (==) :: Eq a => a -> a -> Bool with variables like first and second, if these do not have a value. What you need here is pattern matching.

We can for example use list comprehension:

foo :: Program -> Int
foo list = length [ v | v@(Front _) <- list ]

There is no need to match the parameter with Val {}, or Val _ _ _, etc. since the only data constructor for the Val type is Val.

In case you think you will later add more data constructors, you can however add an extra subpattern:

foo :: Program -> Int
foo list = length [ v | v@(Front (Val {})) <- list ]

Or we can do the pattern matching in a function and use filter :: (a -> Bool) -> [a] -> [a], like:

foo :: Program -> Int
foo = length . filter f
    where f (Front _) = True
          f _ = False

or if we include the Val data constructor check:

foo :: Program -> Int
foo = length . filter f
    where f (Front (Val {})) = True
          f _ = False

As @WillemVanOnsem said in his answer, the approach you're using doesn't work. Try using filter (\x -> case x of { Front _ -> True; Back _ -> False }) list. You may be able to work out how this works from that alone, but if you need any more details:

  • \x -> case x of { Front _ -> True; Back _ -> False } is a lambda expression. It defines an unnamed (or anonymous) function which takes one parameter, named x, and returns the value given by case x of { Front _ -> True; Back _ -> False }.
  • case x of { Front _ -> True; Back _ -> False } does pattern matching on x. If x is of the form Front _, where _ can be anything, then it returns True; otherwise it returns False. Normally this statement would be formatted like this:
case x of
    Front _ -> True
    Back _ -> False

But the compact syntax above is shorter and so works better in this case.

  • The lambda expression above - which as discussed returns True when its argument is of the form Front _ - is then passed to filter as its argument.
Related