Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can a parameter accept any construction of a typeclass but its value can't be constructed conditionally?

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.

like image 943
notquiteamonad Avatar asked Apr 09 '20 16:04

notquiteamonad


1 Answers

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.

like image 59
chi Avatar answered Nov 15 '22 06:11

chi