Haskell 如何为自定义新类型派生PersistField?

Haskell 如何为自定义新类型派生PersistField?,haskell,yesod,Haskell,Yesod,这是一个特定于YesSOD的问题,但即使不知道YesSOD,你也可以帮助我,这与新类型有关 假设我的config/models中有以下简化模型 Value userId UserId weight Weight deriving (Show) 我将在我的webapp中同时使用千克和磅,但我决定DB应该以千克为单位存储东西。为了使类型系统避免混淆两者,我定义了以下内容: newtype Weight = Kilograms Int deriving (Re

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

假设我的config/models中有以下简化模型

Value
  userId       UserId
  weight       Weight
  deriving (Show)
我将在我的webapp中同时使用千克和磅,但我决定DB应该以千克为单位存储东西。为了使类型系统避免混淆两者,我定义了以下内容:

newtype Weight = Kilograms Int
  deriving (Read, Show, Eq, PersistField, PersistFieldSql)
这很好,但我如何从表单中使用它

logForm :: UserId -> Form Value
logForm uid = renderDivs $ Value <$>
    pure uid <*>
    areq intField "Weight" Nothing
我尝试推导
积分
,但它抱怨我没有
实际权重
。一次又一次,我最终得到了:

newtype Weight = Grams Int
   deriving (Read, Show, Eq, Enum, Ord, Num, Integral, Real, PersistField, PersistFieldSql)
这是正确的方法吗?好像有很多重复。有什么更好的方法

一般来说,如果我在Haskell a

newtype N = T a

对于具体类型
a
,如何让
N
重新派生
a
实例中的所有内容,并让
N
派生一些其他类型类(在我的示例中是
PersistField
PersistFieldSql
)。非常感谢。

字段与字段不同。您希望通过导入Yesod.Forms来创建自定义字段。下面是一个MathJax类型的示例;)

newtype MathJax=MathJax{unMathJax::Markdown}
派生(Eq、Ord、Show、Read、PersistField、PersistFieldSql、IsString、Monoid)
unMM::MathJax->Text
unMM=取消标记。取消Mathjax
mathJaxField::(Monad m,RenderMessage(HandlerSite m)FormMessage)=>字段m MathJax
mathJaxField=字段
{fieldParse=parseHelper$Right.MathJax.Markdown.Text.filter(/='\r')
,fieldView=\theId name attrs val\u isReq->toWidget
[哈姆雷特|$newline从不
#{任一id unMM val}
|]
,fieldEnctype=UrlEncoded
}

嘿,谢谢你的帮助。但是,您确定这是正确的方法吗?在这种情况下,底层类型只是一个简单的Int,而不是Markdown(为此您必须编写自己的fieldParse/fieldView?),是的,您必须编写自己的字段。字段类型将解析器(fieldParse记录)和呈现器(fieldView记录)组合在一起。因此,您需要将URL编码的Int解析为Int,然后再解析为newtype。同样,您需要打开新类型。但这是一个“最小”的案例。这是不可推导的,大概是因为您可能希望使用不同的方式来呈现或解析您的类型。这很奇怪,我在问题中所写的内容确实有效–我通常可以使用
intField
并将其保存到DB–然后当我从DB中取回它并
show
it时,我确实看到这个字段用
千克
包裹起来。另一方面,将新类型更改为
kg Double
,然后适当地使用
双字段
,我不断发现
无法将类型“Double”与“ModelTypes.Weight”匹配。为什么它适用于
Int
而不是
Double
,这很奇怪。我最终编写了自定义字段,就像您在这里展示的那样,所以接受了。如果您想办法对具有基础Int的新类型使用普通Int字段,而不必创建自定义新字段并保持类型安全,请告诉我。
newtype N = T a
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
    }