我正在尝试为 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。感谢您的帮助,如果我能更清楚,请告诉我。

最佳答案

啊,类型族和非注入(inject)性。

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

f { fieldView = (injectClass $ fieldView f)}

问题不在于 injectClass 可能会改变 m 是什么。问题是它不知道 m 是什么。它的输入,fieldView f 和上下文,设置 fieldViewf 字段,都只告诉它 HandlerSite m 是什么,简单的事实是你无法从那里算出 m 是什么。就像你有以下情况:
type family F a
type instance F Int = Bool
type instance F Char = Bool

现在尝试传递 F Int 会成功,因为它们都只是 F Char 。因此,即使 Bool 不是 HandlerSite m0 ,尝试传递 HandlerSite m 也可能会成功!因此,类型检查器无法确定 m 应该与 m0 相同,因此您会得到一个不明确的类型错误。

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

你能从 m 的类型签名中完全删除 HandlerSite m ,只用一个普通的类型变量 HandlerSite m 代替它吗?那应该可以解决您的问题,但我不确定它是否会产生任何新问题。

关于twitter-bootstrap - Haskell 相等约束,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18605814/

10-11 14:03