4

是否可以给 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 询问,或者不?)

4

1 回答 1

3

类型签名看起来那样的原因是 a 有两个方面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 形式中是有意义的,但希望有一个更好的名称;)。

于 2012-06-29T03:14:59.380 回答