Question

This is a Yesod-specific question, but even without knowing Yesod you might be able to help me, it has to do with newtypes.

Say I have the following simplified model in my config/models

Value
  userId       UserId
  weight       Weight
  deriving (Show)

I will be using both kilograms and pounds in my webapp, but I decided that the DB should store things in kilograms. To get the type-system to protect me from confusing the two, I define the following:

newtype Weight = Kilograms Int
  deriving (Read, Show, Eq, PersistField, PersistFieldSql)

That compiled fine, but how can I use this from a form?

logForm :: UserId -> Form Value
logForm uid = renderDivs $ Value <$>
    pure uid <*>
    areq intField "Weight" Nothing

I get the error

No instance for (Integral ModelTypes.Weight)
  arising from a use of `intField'

I tried deriving Integral but then it complains I don't have Real Weight. On and on, I end up with:

newtype Weight = Grams Int
   deriving (Read, Show, Eq, Enum, Ord, Num, Integral, Real, PersistField, PersistFieldSql)

Is this the correct way to do it? It seems like a lot of repetition. What's a better way to do it?

In general, if I have in Haskell a

newtype N = T a

for a concrete type a, how can I have N re-derive everything that a is in instance of, and also get N to derive some other typeclasses (in my example PersistField and PersistFieldSql). Thanks a lot.

Was it helpful?

Solution

A PersistField isn't the same as a Field. You want to make a custom field by importing Yesod.Forms. Here's an example, for a MathJax type ;)

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
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top