user4709293
user4709293

Reputation:

How to use high order functions with data types in Haskell

Consider the following data type.

data Movement = Credit Float | Debit Float
    deriving Show

And now this function.

retrieveBiggerThan :: [Movement] -> Float -> [Movement]
retrieveBiggerThan l k = (filter (\(Credit c) -> c>k) l) ++ (filter (\(Debit c) -> c>k) l)

If I test this, I receive as output "non-exhaustive patterns in lambda":

retrieveBiggerThan [Credit 5.5, Debit 2.3, Credit 3.3] 2

[Credito 5.5*** Exception: ficha7.hs:116:35-53: Non-exhaustive patterns in lambda

I believe this happens because filter is only expecting for one of the constructors defined in data Movement. Is there any chance of doing something like this (a filter able to consider both constructors while checking the list)?

filter (\((Credit c) || (Debit c)) -> c > k) listofmovements

Upvotes: 2

Views: 119

Answers (3)

Erik Kaplun
Erik Kaplun

Reputation: 38217

you can create a helper called absMovement and then just trivially filter using that:

absMovement :: Movement -> Float
absMovement (Credit c) = c
absMovement (Debit  d) = d

retrieveBiggerThan k =
  filter (\x -> absMovement x > k)

(note the curried definition of the function with also its argument order flipped — this is more idiomatic in Haskell for many practical reasons which are beyond the scope of this post)

you can also go point free(r) if you prefer:

retrieveBiggerThan k =
  filter $ (k <) . absMovement

which reads: "retrieving bigger than k [of movements] amounts to filtering [of movements] whose absMovement is greater tham k" — the bracketed phrases correspond to the arguments that were "curried away".

P.S. totally missed that actually using record syntax, you can obtain the absMovement function for free, as Daniel Wagner points out; but then again it's nice to think of this as absing, in my opinion.

Upvotes: 2

Daniel Wagner
Daniel Wagner

Reputation: 152847

In this specific case, you could use record syntax.

data Movement = Credit { value :: Float } | Debit { value :: Float }
retrieve l k = filter (\mvmt -> value mvmt > k) l

Upvotes: 5

Chad Gilbert
Chad Gilbert

Reputation: 36375

You can use the LambdaCase extension to make the lambda a bit more concise:

{-# LANGUAGE LambdaCase #-}

data Movement = Credit Float | Debit Float
    deriving Show

foo = map (\case { Credit f -> f; Debit d -> d}) [Credit 1, Debit 2] 

Upvotes: 5

Related Questions