是否可以为checkM提供以下类型:
checkM :: RenderMessage master msg =>
(a -> GHandler sub master (Either msg b)) ->
Field sub master a -> Field sub master b
原因如下:
我有一个要求输入用户名的表单。使用checkM,我立即在数据库中查找输入的用户是否存在:
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!
但是,我只能返回input :: Text,所以在表单接受用户输入之后,我需要为同一个名称执行另一个数据库查找以获取该用户的数据库密钥,这就是我的真实内容想要的。
(这个例子很大程度上简化了。基本上,我想获取一系列不同用户输入的数据库密钥(全部采用一种形式),我只能问它是否为TextFields?)
答案 0 :(得分:3)
类型签名看起来是这样的原因是Field
有两个方面:你如何解析它,以及如何渲染它。 checkM
仅更改解析字段的方式,但呈现函数(fieldView
)保持不变。因此,值需要保持相同的类型。
我能想到获得所需行为的最简单方法是使用一个函数,该函数可以从新类型的值中获取旧类型的值。这样,给定一个新值,我们可以将该函数应用于它并获取旧值以进行渲染。这是代码的样子:
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
}
因此,在您的情况下,您可以通过将userexists
中的最后一行更改为:
(Just (Entity uid _)) -> Right (input, uid)
然后将userField
定义为
userField = checkM' userexists fst textField
我认为像checkM
这样的函数有意义包含在yesod-form中,但希望有更好的名称;)。