aaronlevin
aaronlevin

Reputation: 1443

Haskell + Yesod: Separating insert logic using Persistent won't compile. Cannot figure out types

I'm new to Haskell, coming from Scala. I like Haskell, but I feel like I'm fighting the type system when it comes to using persistent.

My Request: I'd like to separate some insert logic into its own method. I can't quite figure out the types, or the right way to do this. All my failed attempts won't compile. More succinct questions below.

Here is the data declaration:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Curator
  name String
  url String
  feed String
  UniqueUrl url
  deriving Show
Article
  url String
  title String
  content String
  curatorId CuratorId Eq
  deriving Show
|]

Here's a failed attempt that doesn't work:

insertArticle :: String -> String -> String -> MaybeT (???)
insertArticle url title content = do
    curatorId <- selectFirst [curatorName ==. "Fake Name"]
    lift $ do
        curator <- curatorId
        insert (Article url title content curator)

So, my questions:

  1. What type should be in ??? ?
  2. Is lift in the right place? (usually the compiler is more helpful).
  3. Is there a better way to do this?

PS - I have successfully abstracted other logic away, e.g. The insert is just causing me a world of pain. I was unable to get it to compile while using SqlPersistM

getFeeds :: SqlPersistM [Curator]
getFeeds = do
  curatorIds <- selectList [] [Asc CuratorName]
  let curatorGenerics = map entityVal curatorIds
  let curators = map (\x -> x :: Curator) curatorGenerics
  return curators

Upvotes: 2

Views: 335

Answers (1)

snak
snak

Reputation: 6703

The return type of insertArticle should be SqlPersistM (Maybe ArticleId), because it returns Just an inserted article id or Nothing in SqlPersistM monad.

You can implement the function something like:

insertArticle :: String -> String -> String -> SqlPersistM (Maybe ArticleId)
insertArticle url title content = do
    curatorEntity <- selectFirst [CuratorName ==. "Fake Name"] []
    for curatorEntity $ \(Entity curatorId _) ->
        insert (Article url title content curatorId)

I use for from Data.Traversable to handle the Maybe value selectFirst returns here.

But, actually, I don't like this type signature because it sticks to the sql backend. To make it more generalized, you can write a type annotation like this.

insertArticle :: (Applicative m, PersistQuery m, PersistMonadBackend m ~ PersistEntityBackend Curator) =>
                 String -> String -> String -> m (Maybe ArticleId)

The signature is a bit complex, but this function works with any backends.

By the way, your getFeeds can be simplified.

getFeeds :: (Functor m, PersistQuery m, PersistMonadBackend m ~ PersistEntityBackend Curator) =>
            m [Curator]
getFeeds = map entityVal <$> selectList [] [Asc CuratorName]

Upvotes: 1

Related Questions