这是一个特定于Yesod的问题,但是即使不知道Yesod,您也可以帮助我,它与新类型有关。

说我的配置/模型中有以下简化模型

Value
  userId       UserId
  weight       Weight
  deriving (Show)

我将在我的web应用程序中同时使用千克和磅,但是我决定数据库应该以千克存储东西。为了获得防止我混淆两者的类型系统,我定义以下内容:
newtype Weight = Kilograms Int
  deriving (Read, Show, Eq, PersistField, PersistFieldSql)

编译很好,但是如何从表单中使用它呢?
logForm :: UserId -> Form Value
logForm uid = renderDivs $ Value <$>
    pure uid <*>
    areq intField "Weight" Nothing

我得到了错误
No instance for (Integral ModelTypes.Weight)
  arising from a use of `intField'

我尝试派生Integral,但随后它抱怨我没有Real Weight。不断地,我最终得到:
newtype Weight = Grams Int
   deriving (Read, Show, Eq, Enum, Ord, Num, Integral, Real, PersistField, PersistFieldSql)

这是正确的方法吗?似乎有很多重复。有什么更好的方法呢?

一般来说,如果我在Haskell有一个
newtype N = T a

对于具体的a类型,我如何才能让N重新推导a所包含的所有内容,还如何让N派生其他一些类型类(在我的示例PersistFieldPersistFieldSql中)。非常感谢。

最佳答案

PersistField与Field不同。您想通过导入Yesod.Forms创建一个自定义字段。这是MathJax类型的示例;)

newtype MathJax = MathJax { unMathJax :: Markdown }
  deriving (Eq, Ord, Show, Read, PersistField, PersistFieldSql, IsString, Monoid)

unMM :: MathJax -> Text
unMM = unMarkdown . unMathJax

mathJaxField :: (Monad m,  RenderMessage (HandlerSite m) FormMessage) => Field m MathJax
mathJaxField = Field
    { fieldParse = parseHelper $ Right . MathJax . Markdown . Text.filter (/= '\r')
    , fieldView  = \theId name attrs val _isReq -> toWidget
      [hamlet|$newline never
        <textarea id="#{theId}" name="#{name}" *{attrs}>#{either id unMM val}
      |]
    , fieldEnctype = UrlEncoded
    }

08-25 23:27
查看更多