I have this code
data Container = Box Int | Bag Int
inBox :: [Container] -> Int
inBox [] = 0
inBox (x:ls) | (Box i) <- x = i + inBox ls
| otherwise = inBox ls
inBag :: [Container] -> Int
inBag [] = 0
inBag (x:ls) | (Bag i) <- x = i + inBag ls
| otherwise = inBag ls
Clearly InBox
and InBag
have the same structure. I would like to make a function that encompass both of them. What I don't know how to get is to pass the constructor (either Box
or Bag
) as a parameter.
Ideally, the general function woul look something like this:
inSome :: Constructor -> [Container] -> Int
inSome con [] = 0
inSome con (x:ls) | (con i) <- x = i + inSome con ls
| otherwise = inSome con ls
Obviously that doesn't work because constructor is not a defined type here. How can I do it?
One idea is to pass it as a function like so:
inSome :: (Int -> Container) -> [Container] -> Int
inSome _ [] = 0
inSome con (x:ls) | (con i) <- x = i + inSome ls
| otherwise = inSome ls
But then I get the error:
Parse error in pattern: con
Because it cannot match on functions like that.
The reason I want to do this is because I have a complicated data type that includes binary operations (e.g. +, #, ::, etc...) I have several functions that are almost identical for these constructors. I would hate to write all of them and modify them all together. There must me a way to do it in a function. Maybe someone can suggest another approach in the comments?
You can avoid using pattern matching here entirely.
data Container = Box Int | Bag Int
unBox, unBag :: Container -> Maybe Int
unBox (Box i) = Just i
unBox _ = Nothing
unBag (Bag i) = Just i
unBag _ = Nothing
The type of these functions captures the need to get the contained Int
out while switching on the structure of the Container
. This can then be used to build the functions you desire.
inSome :: (Container -> Maybe Int) -> [Container] -> Int
inSome get [] = 0
inSome get (x:ls) = fromMaybe 0 (get x) + inSome ls
inBag = inSome unBag
inBox = inSome unBox
As leftroundabout noted, the pattern of "get or fail" is (massively) generalized in the concept of the Lens
, or, in this case, the Prism
. In general, Prism
s can form a weak kind of first-class pattern, but their use here would be certain overkill.
You might like first-class-patterns, a package that lets you pass and munge patterns.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With