Search code examples
haskellmodelingsum-type

How to pattern match on Constructors in Haskell?


I have a state machine where states are implemented using a sum type. Posting a simplified version here:

data State = 
    A { value :: Int } 
  | B { value :: Int }
  | C { other :: String } 

most of my functions are monadic consuming States and doing some actions based on the type. Something like (this code doesn't compile):

f :: State -> m ()
f st= case st of  
  s@(A | B) -> withValueAction (value s)
  C -> return ()

I know that I could unroll constructors like:

f :: State -> m ()
f st= case st of  
  A v -> withValueAction v
  B v -> withValueAction v
  C _ -> return ()

But that's a lot of boilerplate and brittle to changes. If I change the parameters to the constructor I need to rewrite all case .. of in my codebase.

So how would you pattern match on a subset of constructors and access a shared element?


Solution

  • This is the solution I've picked at the end. My two main requirements were:

    1. "Or" pattern matching over constructors
    2. Selection of a subset of fields shared by the pattern match

    As reported by @Noughtmare 1 is not possible at the moment https://github.com/ghc-proposals/ghc-proposals/pull/522.

    Since for my problem the source of variability comes mostly from parameters in the constructors and not from the number of states, the solution I picked was to enable NamedFieldPuns extension, so the solution is something like:

    f :: State -> m ()
    f st= case st of  
      A {value} -> withValueAction value
      B {value} -> withValueAction value
      C {} -> return ()
    

    It has some boilerplate enumerating constructors but at least it has none at the constructor parameters. I'll have a look at the view patterns maybe they are useful when the source of variability comes from the number of constructors and not the arguments.