Reputation: 11327
Let's say I have the following data structure and functions:
data Settings = Settings { dbName :: String } deriving Show
-- username to user id lookup
getUserId :: Settings -> String -> Int
getUserId settings username = 1
-- checks permission for a given user id
hasPermission :: Settings -> Int -> Bool
hasPermission settings userId = True
I'd like to be able to chain getUserId
and hasPermission
with some syntactic sugar without having to carry instance of Settings
as I chain the function calls. Something like this:
main = do
let _permission = Settings{dbName="prod"} >>= getUserId "bob" >> hasPermission
print _permission
This (obviously) does not work.
Any go-to patterns for this this?
Upvotes: 4
Views: 119
Reputation: 60543
There is a feature called implicit parameters, which has somewhat gone out of style lately, but I believe it is still supported, which offers a nice solution for this.
{-# LANGUAGE ImplicitParams #-}
data Settings = Settings { dbName :: String } deriving Show
getUserId :: (?settings :: Settings) => String -> Int
getUserId username = 1
hasPermission :: (?settings :: Settings) => Int -> Bool
hasPermission userId = True
main = do
let ?settings = Settings { dbName = "prod" }
print $ hasPermission (getUserId "bob")
See also the implicit configurations paper, which explores this problem in quite some depth, and its corresponding library, reflection
.
Upvotes: 0
Reputation: 233487
The simplest way to address such concerns is, in my opinion, to use partial application, like this:
main = do
let settings = Settings { dbName="prod" }
let getUserId' = getUserId settings
let hasPermission' = hasPermission settings
let _permission = hasPermission' $ getUserId' "bob"
print _permission
If you put the 'common' argument last, however, you can also use the built-in reader monad instance:
main :: IO ()
main = do
let getPermission = (flip getUserId) "bob" >>= (flip hasPermission)
print $ getPermission $ Settings { dbName="prod" }
Here getPermission
is a local function with the type Settings -> Bool
. Typically, I consider the first option (partial application) to be simpler and easier to understand.
Upvotes: 1