Filter by nested field

Viewed 63

My Car type has a subtype 'Part':

data Part =
  Part
    { currency :: Text
    , value    :: Float
    }
  deriving (Generic, Show)

data Car =
  Car
    { brand       :: Text
    , engine      :: Part
    }
  deriving (Generic, Show)

I am trying to access and fiter a List of cars like so:

filterByEnginePrice :: [Car] -> Float -> [Car]
filterByEnginePrice cars threshold =
  Prelude.filter (\car -> engine car <= threshold) cars

However, GHC gives me the following error:

 • Couldn't match expected type ‘Part’ with actual type ‘Float’

Which makes sense because I want to filter by value and not by Part. What would be the syntax to access the value field in the filter function?

1 Answers

You can obtain the value by using the value :: Part -> Float getter:

filterByEnginePrice :: [Car] -> Float -> [Car]
filterByEnginePrice cars threshold = Prelude.filter (\car -> value (engine car) <= threshold) cars

but it is more idiomatic to "unpack" the data constructor in the head of the lambda expression:

filterByEnginePrice :: [Car] -> Float -> [Car]
filterByEnginePrice cars threshold = Prelude.filter (\Car { engine=Part {value=v} } -> v <= threshold) cars

or we can use a point-free expression, as specified by @oisdk:

filterByEnginePrice :: [Car] -> Float -> [Car]
filterByEnginePrice cars threshold = Prelude.filter ((threshold >=) . value . engine) cars
Related