Helpful Tips

IHP Flags

Tell GHC(Haskell Compiler) To Infer Constraints And Implicit Parameters

Let’s say you are working with a controller ApplicationsAction and most actions have similar access control:

    action NewApplicationAction { jobPositionId } = do
        jobPosition <- fetch jobPositionId

        -- Access Control
        jobPositions <- currentCompanyJobPositions
        accessDeniedUnless (get #id jobPosition `elem` (ids jobPositions))

        ...

    action UpdateApplicationAction { applicationId } = do
        application <- fetch applicationId

        -- Access Control
        jobPositions <- currentCompanyJobPositions
        accessDeniedUnless (get #id jobPosition `elem` (ids jobPositions))

        ...

We could start by refactoring the access control logic into a function:

accessDeniedUnlessJobPositionAllowed jobPosition = do
    jobPositions <- currentCompanyJobPositions
    accessDeniedUnless (get #id jobPosition `elem` (ids jobPositions))

And then add a type declaration:

accessDeniedUnlessJobPositionAllowed :: JobPosition -> IO ()
accessDeniedUnlessJobPositionAllowed jobPosition = do
    jobPositions <- currentCompanyJobPositions
    accessDeniedUnless (get #id jobPosition `elem` (ids jobPositions))

However, GHC will give us an error message stating:

...

Application/Helper/Controller.hs:51:21: error:
    * Unbound implicit parameter (?context::ControllerContext)
        arising from a use of `currentCompanyJobPositions'
    * In a stmt of a 'do' block:
        jobPositions <- currentCompanyJobPositions
      In the expression:
        do jobPositions <- currentCompanyJobPositions
           accessDeniedUnless (get #id jobPosition `elem` (ids jobPositions))
      In an equation for `accessDeniedUnlessJobPositionAllowed':
          accessDeniedUnlessJobPositionAllowed jobPosition
            = do jobPositions <- currentCompanyJobPositions
                 accessDeniedUnless (get #id jobPosition `elem` (ids jobPositions))
   |
51 |     jobPositions <- currentCompanyJobPositions
   |

We could explicitly add the ?context::ControllerContext implicit:

accessDeniedUnlessJobPositionAllowed :: (?context::ControllerContext) => JobPosition -> IO ()

Writing out implicit parameters, and other type constrains could become messy and/or irritating, so we could tell GHC to infer it:

accessDeniedUnlessJobPositionAllowed :: _ => JobPosition -> IO ()