Haskell Equality Restriction

I am trying to create bootstrap 3 compatibility for Yesod. However, it is not possible to do this using the "renderBootstrap3" function, because you cannot add a class to the inputs. So, I chose to create bootable versions of fields in Form.Fields . The idea is that I can clone normal fields, but add a class declaration to the attribute array. Here is the relevant code:

 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 fab attrs de = fab attrs de textField :: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field m Text textField = addInputClass F.textField addInputClass :: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field ma -> Field ma addInputClass f = f { fieldView = (injectClass $ fieldView f)} 

So, I intend to accept the normal version of the text field and use the write syntax to change only the fieldView method. This method should be replaced with one that is identical, except for adding a class attribute. This is not yet implemented in the code above. It probably looks something like this:

 injectClass fab attrs de = fab (("class", "form-control") : attrs) de 

In any case, the problem is that the source code will not compile. I get an equality constraint error:

 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 ma -> Field ma 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 ma 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)} 

Note that FieldViewFunc ma is defined as

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

So I'm not far off. The problem (I think) is that it does not confirm that injectClass does not change the monad. However, this should be obvious to the compiler. The typical signature for injectClass is clear. I am looking for what I need to do to satisfy the GHC. Thanks for any help, and let me know if I can be more clear.

+4
source share
2 answers

Ah, family types and non-injectivity.

Here's what happens: we are trying to check the type

 f { fieldView = (injectClass $ fieldView f)} 

The problem is not that injectClass can change what m . The problem is that he does not know what m . His contribution, fieldView f and context, setting the field fieldView f , say only that HandlerSite m , and a simple fact: you can not determine that m from there. You like it if you have the following:

 type family F a type instance F Int = Bool type instance F Char = Bool 

Now, an attempt to pass F Int where F Char is expected will succeed because they are both just Bool . Therefore, an attempt to pass a HandlerSite m0 , where a HandlerSite m is expected, may succeed even if m not m0 ! Therefore, the type checker cannot be sure that m0 should be the same as m , so you get an ambiguous type error.

In addition, you cannot easily eliminate the ambiguity using manual annotations, since you will encounter the same problem: you will tell the type controller that HandlerSite m should be what it already knows.

You can completely remove HandlerSite m from a signature of type injectClass , replacing it with just a regular variable of type h , say? This should solve your problem, but I'm not sure that it will create new ones.

+3
source

There is a simpler solution. You can override areq and aopt to automatically add the necessary classes:

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

Source: https://habr.com/ru/post/1500435/


All Articles