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, Prisms 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