This blog runs on a barebones blogging framework that I knocked together using Yesod 1.1 back in 2013. I recently ported it over to Yesod 1.4. Apart from the few changes that I have detailed below, everything worked straight away. Refactoring code in Haskell is a very different experience compared to fully dynamic languages.

Here are some notes on the changes that I encountered between Yesod 1.1 and 1.4. Perhaps these will be useful for someone.

aformM Link to heading

Previously I used aformM to get the current time in a form:

commentForm :: EntryId -> Form Comment
commentForm entryId = renderDivs $ Comment
     pure entryId
     aformM (liftIO getCurrentTime)
     areq textField (fieldSettingsLabel MsgCommentName) Nothing
     aopt emailField (fieldSettingsLabel MsgCommentEmail) Nothing
     aopt urlField (fieldSettingsLabel MsgCommentUrl) Nothing
     areq htmlField (fieldSettingsLabel MsgCommentText) Nothing
     pure False <* recaptchaAForm

Now, use lift (liftIO getCurrentTime):

commentForm :: EntryId -> Form Comment
commentForm entryId = renderDivs $ Comment
     pure entryId
    --  aformM (liftIO getCurrentTime)
     lift (liftIO getCurrentTime)
     areq textField (fieldSettingsLabel MsgCommentName) Nothing
     aopt emailField (fieldSettingsLabel MsgCommentEmail) Nothing
     aopt urlField (fieldSettingsLabel MsgCommentUrl) Nothing
     areq htmlField (fieldSettingsLabel MsgCommentText) Nothing
     pure False <* recaptchaAForm

MinLen Link to heading

Some new names clash with the Prelude, e.g. maximum is not the usual function from the Prelude, but rather something from Data.MinLen that encodes type-level natural numbers.

*Main> :t maximum
maximum :: MonoFoldableOrd mono => MinLen (Succ nat) mono -> Element mono

*Main> :t P.maximum
P.maximum :: Ord a => [a] -> a

No unKey or PersistInt64 Link to heading

Persistent values in the old system looked like this:

Entity {entityKey = Key {unKey = PersistInt64 1},
        entityVal = title: "first post" mashed title: "first-post" year: 2015 month: 8 day: 14 content: "Hi there!" visible: False}

Entity {entityKey = Key {unKey = PersistInt64 2},
        entityVal = title: "second post" mashed title: "second-post" year: 2015 month: 8 day: 14 content: "Hi there! Do de dah!" visible: False}

and we could use PersistInt64 to construct the value, or unKey to deconstruct it.

*Main> :t PersistInt64
PersistInt64 :: GHC.Int.Int64 -> PersistValue

*Main> :t unKey
unKey :: KeyBackend backend entity -> PersistValue

Now values look like:

Entity {entityKey = EntryKey {unEntryKey = SqlBackendKey {unSqlBackendKey = 1}}, entityVal = "first post"}
Entity {entityKey = EntryKey {unEntryKey = SqlBackendKey {unSqlBackendKey = 2}}, entityVal = "second post"}

Old code like

foo :: PersistValue -> GHC.Int.Int64
foo (PersistInt64 i) = i

niceEntryId :: KeyBackend backend entity -> String
niceEntryId x = show $ foo $ unKey x

becomes

niceEntryId :: Key Entry -> Text
niceEntryId = DT.pack . show . unSqlBackendKey . unEntryKey

We could also use toPathPiece:

*Main> :t toPathPiece :: Key Entry -> Text
toPathPiece :: Key Entry -> Text :: Key Entry -> Text

If you’re wondering how to find such a thing, look at the output of :info Key in ghci which includes these lines:

instance PathPiece (Key User) -- Defined at Model.hs:10:1
instance PathPiece (Key Entry) -- Defined at Model.hs:10:1
instance PathPiece (Key Comment) -- Defined at Model.hs:10:1

I believe that the more general option is fromSqlKey:

unKey' :: ToBackendKey SqlBackend record => Key record -> Text
unKey' = DT.pack . show . fromSqlKey

This should work over any SQL backend, unlike the older code that was tied to the particular implementation (e.g. 64bit ints).

Similarly, old code constructed a Key from an integer:

let entryId = Key $ PersistInt64 (fromIntegral i)

New code uses toSqlKey since the PersistInt64 constructor isn’t available:

let entryId = toSqlKey i :: Key Entry

Original blog framework, written with Yesod 1.1.9.4: https://github.com/carlohamalainen/cli-yesod-blog.

New blog framework, compiles against Yesod 1.4: https://github.com/carlohamalainen/cli-yesod-blog-1.4.