haskell

Pattern match where type has constructors


I'm using a 3rd party library which has the following types:

data ServicesRequest req contentType res accept = ServicesRequest
  { rMethod  :: NH.Method   
  , rUrlPath :: [BCL.ByteString] 
  , rParams   :: Params 
  , rAuthTypes :: [P.TypeRep] 
  }

I'm trying to extract the req from a list of ServicesRequest (serviceRequests), similar to this:

let reqs = map (\(ServicesRequest req contentType res accept) -> req) serviceRequests 

Unfortunately, this produces a list of NH.Method's, the first constructor to ServiceRequest.

I've tried different variations to get the `req', but they all give 'parse error in pattern':

let reqs = map (\(ServicesRequest{} req contentType res accept) -> req) serviceRequests 

let reqs = map (\((ServicesRequest _ _ _ _) req contentType res accept) -> req) serviceRequests 

How can I pattern match on the req without it being confused as rMethod constructor?


Solution

  • I'm trying to extract the req from a list of ServicesRequest

    There are no reqs in a ServicesRequest, so this isn't possible.

    Is it possible to pattern match a type?

    By default, there's no pattern matching at the type level. You can certainly write types that ensure that the req type is the same one as in a ServicesRequest -- just use the same name in both places you want it. For example:

    constrainedId :: ServicesRequest req contentType res accept -> req -> req
    constrainedId _ = id
    

    Such restricted types can be useful. Check out ScopedTypeVariables if you need to annotate types inside the body of your function with a req from the top-level type annotation.

    For more advanced uses, type families offer something that looks a bit like type-level pattern matching. For example:

    type family ReqFromSR a where
        ReqFromSR (ServicesRequest req contentType res accept) = req
    

    You could then write approximately the same type for constrainedId in a slightly more compact form:

    constrainedId :: sr -> ReqFromSR sr -> ReqFromSR sr
    -- OR
    constrainedId :: ReqFromSR sr ~ req => sr -> req -> req
    constrainedId _ = id