Is Form Field conversion possible in yesod?
haskell, yesod
Solution
The reason the type signature looks that way is that there are two aspects to a `Field`: how you parse it, and how you render it. `checkM` only changes how you parse the field, but the rendering function (`fieldView`) remains unmodified. Therefore, the value needs to keep the same type.
The simplest way I can think of to get the behavior you want is to have a function which can get a value of the old type from a value of the new type. That way, given a new value, we can just apply that function to it and get the old value for rendering purposes. Here's what the code would look like:
checkM' :: RenderMessage master msg
=> (a -> GHandler sub master (Either msg b))
-> (b -> a)
-> Field sub master a
-> Field sub master b
checkM' f inv field = field
{ fieldParse = \ts -> do
e1 <- fieldParse field ts
case e1 of
Left msg -> return $ Left msg
Right Nothing -> return $ Right Nothing
Right (Just a) -> fmap (either (Left . SomeMessage) (Right . Just)) $ f a
, fieldView = \i n a eres req -> fieldView field i n a (fmap inv eres) req
}
So in your case, you could use it by changing the last line in `userexists` to:
(Just (Entity uid _)) -> Right (input, uid)
and then defining `userField` as
userField = checkM' userexists fst textField
I think a function like `checkM` makes sense to include in yesod-form, but hopefully with a better name ;).
Problem
Would it be possible to give checkM the following type instead: ``` checkM :: RenderMessage master msg => (a -> GHandler sub master (Either msg b)) -> Field sub master a -> Field sub master b ``` The reason is the following: I have a form that asks for a user name. Using checkM, I immediately look up in the database whether the entered user exists: ``` userField = checkM userexists textField userexists input = do mbuser <- runDB $ getBy $ UniqueName input return $ case mbuser of Nothing -> Left ("This user does not exist!" :: Text) (Just (Entity uid _)) -> Right input -- I would like to write "return Right uid" above! ``` However, I can only return input::Text, so right after the form has accepted the user input, I need to do another database lookup for the same name to get the database key for that user, which is what I really wanted. (This example is largely simplified. Essentially, I want to get the database keys for a series of different user inputs (all in one form), which I can only ask as TextFields, or not?)