Reputation: 983
Keeping in mind that I never succeded in understanding what a monad is, I'm having a problem with Yesod. I have a form that I would need to use in multiple Handlers, so I'm trying to define it in a module that I could import when needed. That form is using a bunch of not exported functions to create custom Fields. When trying to run simple database operation like I'm used to inside my Handlers (trying to get default values for the fields), I get the following error :
Couldn't match expected type ‘Maybe (Entity Client)’
with actual type ‘HandlerT site0 IO (Maybe (Entity Client))’
In a stmt of a 'do' block: runDB $ selectFirst [ClientId ==. id] []
In the expression: do { runDB $ selectFirst [ClientId ==. id] [] }
In a case alternative:
Just id -> do { runDB $ selectFirst [ClientId ==. id] [] }
I tried a bunch of stuff, using <- doesn't seem to change anything. I think that I might not be inside the correct monad ? I googled a lot and as I understand it, runDB will only work inside the Handler monad, but I have no idea how to get in that, if at all possible.
I have multiple Handlers importing that module, calling that function to generate an AForm, if that matters.
Thanks a lot !
EDIT : Here is the function raising that error :
getClientFromId :: Maybe (Key Client) -> Maybe (Entity Client)
getClientFromId mId = do
case mId of
Nothing -> Nothing
Just id -> do
runDB $ selectFirst [ClientId ==. id] []
So after reading the answers, I tried removing the signature : no change. I tried using the different suggested signatures but nothing worked. Here is what I have currently :
getClientFromId :: Maybe (Key Client) -> Handler (Maybe (Entity Client))
getClientFromId mId = do
case mId of
Nothing -> return Nothing
Just id -> do
client <- runDB $ selectFirst [ClientId ==. id] []
return client
I'm calling that from a ternary inside the definition of an AForm :
<*> (fmap entityKey <$> aopt clientIdField (addIdToField "ClientIdField" (bfs ("Owner" :: Text))) (isJust mHorse ? (Just (getClientFromId $ horseClient $ fromJust mHorse)) :? Nothing))
The ternary isn't very easy to read, if I figure out how to make all of this work I'll probably change that :). I get that error :
Couldn't match type ‘HandlerT App IO (Maybe (Entity Client))’
with ‘Maybe (Entity Client)’
Expected type: Maybe (Entity Client)
Actual type: Handler (Maybe (Entity Client))
In the first argument of ‘Just’, namely
‘(getClientFromId $ horseClient $ fromJust mHorse)’
In the first argument of ‘(:?)’, namely
‘(Just (getClientFromId $ horseClient $ fromJust mHorse))’
In the second argument of ‘(?)’, namely
‘(Just (getClientFromId $ horseClient $ fromJust mHorse))
:? Nothing’
Looking at that, it seems that I have the correct return types except there is a "HandlerT site0 IO" on top of it. I tried using an intermediary function with a <- to remove it but that's not working. I'm guessing my actual problem here is that I don't understand monads, but I looked at other Yesod projects that work and I really don't get what I'm doing differently.
(I know that's nesting multiple Maybes, but in my DB horseClient is a maybe, and the default value of a form is waiting for a Maybe, so it seems "normal" that the final value should be a Maybe (Maybe (Entity Client)) even if that's a bit strange)
Upvotes: 0
Views: 179
Reputation: 2983
Let's look at the types.
selectFirst :: (PersistEntity val, PersistEntityBackend val ~ PersistMonadBackend m) =>
[Filter val] ->
[SelectOpt val] ->
m (Maybe (Entity val))
runDB :: YesodDB site a -> HandlerT site IO a
type YesodDB site = ReaderT (YesodPersistBackend site) (HandlerT site IO)
The m
in your selectFirst
will be YesodDB site
which is an alias for a ReaderT
that I'm guessing provides the database connection.
I think the correct type for your function should be
Maybe (Key Client) -> HandlerT site IO (Maybe (Entity Client))
site
will be replaced with your Yesod instance type.
First of all, this is pretty ugly:
(isJust mHorse ? (Just (getClientFromId $ horseClient $ fromJust mHorse)) :? Nothing)
You could do this instead:
fmap (justClientFromId . horseClient) mHorse
However, that brings us to the second problem. justClientFromid
returns a Handler
wrapping the value you want.
I think the main problem stems from your first statement:
Keeping in mind that I never succeded in understanding what a monad is
You should probably read the Yesod documentation and try to understand what's going on.
Upvotes: 0
Reputation: 52039
You declared getClientId
as a pure function, but you are also calling runDB
which performs I/O, so it can't be a pure function. Intuitively that's why it doesn't type check.
I suggest that you write getClientId
in isolation without a type signature and see what GHC infers. My guess is that it will be something like:
Maybe (Key Client) -> Handler (Maybe (Entity Client))
That means everywhere you call it you'll need to do something like:
mid' <- getClientFromId mid
Upvotes: 1