In Haskell how to pass a constructor as a parameter and match over it

constructor, haskell

Solution

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.

Problem

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?

Original source