4

I'm fairly new to Haskell, though not to programming, and I've been using the req library to perform HTTPS requests.

In order to retain some generality, there will be two types of request - one to create a document (via HTTP POST) and another to update a document (via HTTP PATCH, using a non-empty monoid in the updateMask parameter).

I can infer the HTTP verb from whether updateMask == mempty, but this won't compile because POST and PATCH are different data declarations (although both are valid as the first parameter of req because they are instances of HttpMethod.

getSaveEventRequestResponse :: Text -> Option Https -> Document -> IO IgnoreResponse
getSaveEventRequestResponse authToken updateMask document =
  runReq defaultHttpConfig $
  req
    (if updateMask == mempty
       then POST
       else PATCH)
    (https "test.example.com" /: "api" /: "projects" /: "myproject")
    (ReqBodyJson document)
    ignoreResponse $
  oAuth2Bearer (encodeUtf8 authToken) <> updateMask

If I swap out the if conditional for either one of POST or PATCH the code compiles without error.

Is there a way to make the compiler allow this conditional response or do I have to duplicate this function, one using the POST variant and another using the PATCH?


Edit for the benefit of anyone who comes to this and tries to use the same code:

The condition I used (updateMask == mempty) is not actually valid here, but that's not relevant to the question. The question stands if this condition is replaced by True or False.


Edit 2 regarding the linked question. While I now, having got the answer, see how closely linked it is, it relies on having already considered partial application. While the principle is the same, the introduction of partial application makes it difficult for a beginner with Haskell to apply the answer there to this context.

duplode
  • 33,731
  • 7
  • 79
  • 150
notquiteamonad
  • 1,159
  • 2
  • 12
  • 28
  • 2
    Does this answer your question? [Sum types - Why in Haskell is \`show (Int | Double)\` different than \`(show Int) | (show Double)\`](https://stackoverflow.com/questions/58783024/sum-types-why-in-haskell-is-show-int-double-different-than-show-int) – Joseph Sible-Reinstate Monica Apr 09 '20 at 16:11
  • If you **really** don't want to duplicate the function, you could reach for `ExistentialQuantification`, but that will make a mess of its own. – Joseph Sible-Reinstate Monica Apr 09 '20 at 16:13
  • @JosephSible-ReinstateMonica I understand the reasoning based on that that the types are different. Is that to say that it is indeed impossible to do this without a complete duplication (excluding what has been mentioned on `ExistentialQuantification`? – notquiteamonad Apr 09 '20 at 16:15
  • 1
    You could try `(if .. then req POST else req PATCH) other args here`. This might require some type annotation to work, or simply be a hopeless attempt. I'm not familiar enough with the library to understand if this can succeed: if `AllowsBody POST` and `AllowsBody POST` are the same, it might work. – chi Apr 09 '20 at 16:38
  • @chi Brilliant answer, this works perfectly! Cheers – notquiteamonad Apr 09 '20 at 16:43

1 Answers1

7

A possible solution is to use

(if updateMask == mempty then req POST else req PATCH) other args here

This is because

req :: (MonadHttp m, HttpMethod method, HttpBody body, 
        HttpResponse response, 
        HttpBodyAllowed (AllowsBody method) (ProvidesBody body))     
    => method   
    -> Url scheme   
    -> body 
    -> Proxy response   
    -> Option scheme    
    -> m response

Now, AllowsBody POST and AllowsBody PATCH are equal, since both are defined as 'CanHaveBody. Therefore, both req POST and req PATCH can share a common type:

req POST, req PATCH 
    :: (MonadHttp m, HttpBody body, 
        HttpResponse response, 
        HttpBodyAllowed 'CanHaveBody (ProvidesBody body))    
    => Url scheme   
    -> body 
    -> Proxy response   
    -> Option scheme    
    -> m response

Having the same type, they can be used in the two branches of the same if then else.

chi
  • 111,837
  • 3
  • 133
  • 218