Haskell平等约束

时间:2013-09-04 05:10:06

标签: twitter-bootstrap haskell yesod

我正在尝试为Yesod构建bootstrap 3兼容性。但是,通过创建“renderBootstrap3”函数这样做是不可能的,因为您无法将类添加到输入。所以,我选择在Form.Fields中创建字段的引导版本。我的想法是我可以克隆普通字段,但在属性数组中添加一个类声明。以下是相关代码:

import qualified Yesod.Form.Fields as F

injectClass :: (Text -> Text -> [(Text,Text)] -> Either Text a -> Bool -> WidgetT (HandlerSite m) IO ()
           Text -> Text -> [(Text,Text)] -> Either Text a -> Bool -> WidgetT (HandlerSite m) IO ()
injectClass f a b attrs d e = f a b attrs d e

textField :: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field m Text
textField = addInputClass F.textField

addInputClass :: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field m a -> Field m a
addInputClass f = f { fieldView = (injectClass $ fieldView f)}

所以,我的目的是采用正常版本的文本字段并使用记录语法来仅修改fieldView方法。除了添加类属性外,此方法应替换为相同的方法。这尚未在上面的代码中实现。它可能看起来像:

injectClass f a b attrs d e = f a b (("class", "form-control") : attrs) d e

无论如何,问题是原始代码无法编译。我得到一个等式约束错误:

Could not deduce (HandlerSite m0 ~ HandlerSite m)
from the context (Monad m,
                  RenderMessage (HandlerSite m) FormMessage)
  bound by the type signature for
             addInputClass :: (Monad m,
                               RenderMessage (HandlerSite m) FormMessage) =>
                              Field m a -> Field m a
  at Field/Bootstrap.hs:27:18-95
NB: `HandlerSite' is a type function, and may not be injective
The type variable `m0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Expected type: FieldViewFunc m a
  Actual type: Text
               -> Text
               -> [(Text, Text)]
               -> Either Text a
               -> Bool
               -> WidgetT (HandlerSite m0) IO ()
In the `fieldView' field of a record
In the expression: f {fieldView = (injectClass $ fieldView f)}
In an equation for `addInputClass':
    addInputClass f = f {fieldView = (injectClass $ fieldView f)}

请注意FieldViewFunc m a定义为

type FieldViewFunc m a
    = Text -- ^ ID
   -> Text -- ^ Name
   -> [(Text, Text)] -- ^ Attributes
   -> Either Text a -- ^ Either (invalid text) or (legitimate result)
   -> Bool -- ^ Required?
   -> WidgetT (HandlerSite m) IO ()

所以,我并不遥远。问题是(我认为)并非认识到injectClass不会改变monad。但是,这对编译器来说应该是显而易见的。 injectClass的类型签名很清楚。我正在寻找满足GHC需要做的事情。感谢您的帮助,如果我能更清楚,请告诉我。

2 个答案:

答案 0 :(得分:3)

啊,键入家庭和非注入。

这是发生了什么:我们正在尝试打字检查

f { fieldView = (injectClass $ fieldView f)}

问题不在于injectClass可能会改变m的内容。问题是它不会知道 m是什么。它的输入fieldView f和上下文设置了fieldView的{​​{1}}字段,两者都只告诉它f是什么,简单的事实是你无法解决什么问题HandlerSite m来自那里。就像你有以下内容一样:

m

现在尝试传递预计type family F a type instance F Int = Bool type instance F Char = Bool 的{​​{1}}会成功,因为它们都只是F Int。因此,即使F Char不是Bool,尝试传递预期HandlerSite m0的{​​{1}}也可能会成功!因此,类型检查器无法确定HandlerSite m应该与m相同,因此您会收到模糊的类型错误。

此外,你不能通过手动注释轻松解决歧义,因为你会遇到同样的问题:你会告诉类型检查器m0应该是什么,它已经知道了。

您可以完全从m0的类型签名中删除m,将其替换为普通类型变量HandlerSite m吗?这应该解决你的问题,但我不确定它是否会创造任何新问题。

答案 1 :(得分:0)

有一个更简单的解决方案。您可以重新定义areqaopt以自动添加必要的类:

areqBs3 :: (RenderMessage site FormMessage, HandlerSite m ~ site, MonadHandler m)
     => Field m a
     -> FieldSettings site
     -> Maybe a
     -> AForm m a
areqBs3 a b = areq a (b {fsAttrs = [("class", "form-control")]})